/* Expand builtin functions.
Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
This file is part of GCC.
#define CALLED_AS_BUILT_IN(NODE) \
(!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
-/* Register mappings for target machines without register windows. */
-#ifndef INCOMING_REGNO
-#define INCOMING_REGNO(OUT) (OUT)
-#endif
-#ifndef OUTGOING_REGNO
-#define OUTGOING_REGNO(IN) (IN)
-#endif
-
#ifndef PAD_VARARGS_DOWN
#define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN
#endif
const char *const built_in_class_names[4]
= {"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) STRINGX(X),
+#define DEF_BUILTIN(X, N, C, T, LT, B, F, NA, AT, IM) #X,
const char *const built_in_names[(int) END_BUILTINS] =
{
#include "builtins.def"
required to implement the function call in all cases. */
tree implicit_built_in_decls[(int) END_BUILTINS];
-static int get_pointer_alignment PARAMS ((tree, unsigned int));
-static tree c_strlen PARAMS ((tree));
-static const char *c_getstr PARAMS ((tree));
-static rtx c_readstr PARAMS ((const char *,
- enum machine_mode));
-static int target_char_cast PARAMS ((tree, char *));
-static rtx get_memory_rtx PARAMS ((tree));
-static int apply_args_size PARAMS ((void));
-static int apply_result_size PARAMS ((void));
+static int get_pointer_alignment (tree, unsigned int);
+static tree c_strlen (tree, int);
+static const char *c_getstr (tree);
+static rtx c_readstr (const char *, enum machine_mode);
+static int target_char_cast (tree, char *);
+static rtx get_memory_rtx (tree);
+static tree build_string_literal (int, const char *);
+static int apply_args_size (void);
+static int apply_result_size (void);
#if defined (HAVE_untyped_call) || defined (HAVE_untyped_return)
-static rtx result_vector PARAMS ((int, rtx));
+static rtx result_vector (int, rtx);
#endif
-static rtx expand_builtin_setjmp PARAMS ((tree, rtx));
-static void expand_builtin_prefetch PARAMS ((tree));
-static rtx expand_builtin_apply_args PARAMS ((void));
-static rtx expand_builtin_apply_args_1 PARAMS ((void));
-static rtx expand_builtin_apply PARAMS ((rtx, rtx, rtx));
-static void expand_builtin_return PARAMS ((rtx));
-static enum type_class type_to_class PARAMS ((tree));
-static rtx expand_builtin_classify_type PARAMS ((tree));
-static void expand_errno_check PARAMS ((tree, rtx));
-static rtx expand_builtin_mathfn PARAMS ((tree, rtx, rtx));
-static rtx expand_builtin_mathfn_2 PARAMS ((tree, rtx, rtx));
-static rtx expand_builtin_constant_p PARAMS ((tree, enum machine_mode));
-static rtx expand_builtin_args_info PARAMS ((tree));
-static rtx expand_builtin_next_arg PARAMS ((tree));
-static rtx expand_builtin_va_start PARAMS ((tree));
-static rtx expand_builtin_va_end PARAMS ((tree));
-static rtx expand_builtin_va_copy PARAMS ((tree));
-static rtx expand_builtin_memcmp PARAMS ((tree, tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_strcmp PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_strncmp PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx builtin_memcpy_read_str PARAMS ((PTR, HOST_WIDE_INT,
- enum machine_mode));
-static rtx expand_builtin_strcat PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_strncat PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_strspn PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_strcspn PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_memcpy PARAMS ((tree, rtx,
- enum machine_mode, int));
-static rtx expand_builtin_mempcpy PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_memmove PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_bcopy PARAMS ((tree));
-static rtx expand_builtin_strcpy PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_stpcpy PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx builtin_strncpy_read_str PARAMS ((PTR, HOST_WIDE_INT,
- enum machine_mode));
-static rtx expand_builtin_strncpy PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx builtin_memset_read_str PARAMS ((PTR, HOST_WIDE_INT,
- enum machine_mode));
-static rtx builtin_memset_gen_str PARAMS ((PTR, HOST_WIDE_INT,
- enum machine_mode));
-static rtx expand_builtin_memset PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_bzero PARAMS ((tree));
-static rtx expand_builtin_strlen PARAMS ((tree, rtx, enum machine_mode));
-static rtx expand_builtin_strstr PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_strpbrk PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_strchr PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_strrchr PARAMS ((tree, rtx,
- enum machine_mode));
-static rtx expand_builtin_alloca PARAMS ((tree, rtx));
-static rtx expand_builtin_unop PARAMS ((enum machine_mode,
- tree, rtx, rtx, optab));
-static rtx expand_builtin_frame_address PARAMS ((tree, tree));
-static rtx expand_builtin_fputs PARAMS ((tree, int, int));
-static tree stabilize_va_list PARAMS ((tree, int));
-static rtx expand_builtin_expect PARAMS ((tree, rtx));
-static tree fold_builtin_constant_p PARAMS ((tree));
-static tree fold_builtin_classify_type PARAMS ((tree));
-static tree fold_builtin_inf PARAMS ((tree, int));
-static tree fold_builtin_nan PARAMS ((tree, tree, int));
-static int validate_arglist PARAMS ((tree, ...));
-static tree fold_trunc_transparent_mathfn PARAMS ((tree));
-static bool readonly_data_expr PARAMS ((tree));
+static rtx expand_builtin_setjmp (tree, rtx);
+static void expand_builtin_prefetch (tree);
+static rtx expand_builtin_apply_args (void);
+static rtx expand_builtin_apply_args_1 (void);
+static rtx expand_builtin_apply (rtx, rtx, rtx);
+static void expand_builtin_return (rtx);
+static enum type_class type_to_class (tree);
+static rtx expand_builtin_classify_type (tree);
+static void expand_errno_check (tree, rtx);
+static rtx expand_builtin_mathfn (tree, rtx, rtx);
+static rtx expand_builtin_mathfn_2 (tree, rtx, rtx);
+static rtx expand_builtin_constant_p (tree, enum machine_mode);
+static rtx expand_builtin_args_info (tree);
+static rtx expand_builtin_next_arg (tree);
+static rtx expand_builtin_va_start (tree);
+static rtx expand_builtin_va_end (tree);
+static rtx expand_builtin_va_copy (tree);
+static rtx expand_builtin_memcmp (tree, tree, rtx, enum machine_mode);
+static rtx expand_builtin_strcmp (tree, rtx, enum machine_mode);
+static rtx expand_builtin_strncmp (tree, rtx, enum machine_mode);
+static rtx builtin_memcpy_read_str (void *, HOST_WIDE_INT, enum machine_mode);
+static rtx expand_builtin_strcat (tree, rtx, enum machine_mode);
+static rtx expand_builtin_strncat (tree, rtx, enum machine_mode);
+static rtx expand_builtin_strspn (tree, rtx, enum machine_mode);
+static rtx expand_builtin_strcspn (tree, rtx, enum machine_mode);
+static rtx expand_builtin_memcpy (tree, rtx, enum machine_mode);
+static rtx expand_builtin_mempcpy (tree, rtx, enum machine_mode, int);
+static rtx expand_builtin_memmove (tree, rtx, enum machine_mode);
+static rtx expand_builtin_bcopy (tree);
+static rtx expand_builtin_strcpy (tree, rtx, enum machine_mode);
+static rtx expand_builtin_stpcpy (tree, rtx, enum machine_mode);
+static rtx builtin_strncpy_read_str (void *, HOST_WIDE_INT, enum machine_mode);
+static rtx expand_builtin_strncpy (tree, rtx, enum machine_mode);
+static rtx builtin_memset_read_str (void *, HOST_WIDE_INT, enum machine_mode);
+static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, enum machine_mode);
+static rtx expand_builtin_memset (tree, rtx, enum machine_mode);
+static rtx expand_builtin_bzero (tree);
+static rtx expand_builtin_strlen (tree, rtx, enum machine_mode);
+static rtx expand_builtin_strstr (tree, rtx, enum machine_mode);
+static rtx expand_builtin_strpbrk (tree, rtx, enum machine_mode);
+static rtx expand_builtin_strchr (tree, rtx, enum machine_mode);
+static rtx expand_builtin_strrchr (tree, rtx, enum machine_mode);
+static rtx expand_builtin_alloca (tree, rtx);
+static rtx expand_builtin_unop (enum machine_mode, tree, rtx, rtx, optab);
+static rtx expand_builtin_frame_address (tree, tree);
+static rtx expand_builtin_fputs (tree, rtx, bool);
+static rtx expand_builtin_printf (tree, rtx, enum machine_mode, bool);
+static rtx expand_builtin_fprintf (tree, rtx, enum machine_mode, bool);
+static rtx expand_builtin_sprintf (tree, rtx, enum machine_mode);
+static tree stabilize_va_list (tree, int);
+static rtx expand_builtin_expect (tree, rtx);
+static tree fold_builtin_constant_p (tree);
+static tree fold_builtin_classify_type (tree);
+static tree fold_builtin_inf (tree, int);
+static tree fold_builtin_nan (tree, tree, int);
+static int validate_arglist (tree, ...);
+static bool integer_valued_real_p (tree);
+static tree fold_trunc_transparent_mathfn (tree);
+static bool readonly_data_expr (tree);
+static rtx expand_builtin_fabs (tree, rtx, rtx);
+static rtx expand_builtin_cabs (tree, rtx);
+static rtx expand_builtin_signbit (tree, rtx);
+static tree fold_builtin_cabs (tree, tree, tree);
+static tree fold_builtin_trunc (tree);
+static tree fold_builtin_floor (tree);
+static tree fold_builtin_ceil (tree);
+static tree fold_builtin_round (tree);
+static tree fold_builtin_bitop (tree);
+static tree fold_builtin_memcpy (tree);
+static tree fold_builtin_mempcpy (tree);
+static tree fold_builtin_memmove (tree);
+static tree fold_builtin_strcpy (tree);
+static tree fold_builtin_strncpy (tree);
+static tree fold_builtin_memcmp (tree);
+static tree fold_builtin_strcmp (tree);
+static tree fold_builtin_strncmp (tree);
+static tree fold_builtin_signbit (tree);
/* Return the alignment in bits of EXP, a pointer valued expression.
But don't return more than MAX_ALIGN no matter what.
expression is actually pointing at an object whose alignment is tighter. */
static int
-get_pointer_alignment (exp, max_align)
- tree exp;
- unsigned int max_align;
+get_pointer_alignment (tree exp, unsigned int max_align)
{
unsigned int align, inner;
way, because it could contain a zero byte in the middle.
TREE_STRING_LENGTH is the size of the character array, not the string.
+ ONLY_VALUE should be nonzero if the result is not going to be emitted
+ into the instruction stream and zero if it is going to be expanded.
+ E.g. with i++ ? "foo" : "bar", if ONLY_VALUE is nonzero, constant 3
+ is returned, otherwise NULL, since
+ len = c_strlen (src, 1); if (len) expand_expr (len, ...); would not
+ evaluate the side-effects.
+
The value returned is of type `ssizetype'.
Unfortunately, string_constant can't access the values of const char
arrays with initializers, so neither can we do so here. */
static tree
-c_strlen (src)
- tree src;
+c_strlen (tree src, int only_value)
{
tree offset_node;
HOST_WIDE_INT offset;
int max;
const char *ptr;
+ STRIP_NOPS (src);
+ if (TREE_CODE (src) == COND_EXPR
+ && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0))))
+ {
+ tree len1, len2;
+
+ len1 = c_strlen (TREE_OPERAND (src, 1), only_value);
+ len2 = c_strlen (TREE_OPERAND (src, 2), only_value);
+ if (tree_int_cst_equal (len1, len2))
+ return len1;
+ }
+
+ if (TREE_CODE (src) == COMPOUND_EXPR
+ && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0))))
+ return c_strlen (TREE_OPERAND (src, 1), only_value);
+
src = string_constant (src, &offset_node);
if (src == 0)
return 0;
or sum of string constant and integer constant. */
static const char *
-c_getstr (src)
- tree src;
+c_getstr (tree src)
{
tree offset_node;
GET_MODE_BITSIZE (MODE) bits from string constant STR. */
static rtx
-c_readstr (str, mode)
- const char *str;
- enum machine_mode mode;
+c_readstr (const char *str, enum machine_mode mode)
{
HOST_WIDE_INT c[2];
HOST_WIDE_INT ch;
P. */
static int
-target_char_cast (cst, p)
- tree cst;
- char *p;
+target_char_cast (tree cst, char *p)
{
unsigned HOST_WIDE_INT val, hostval;
address located within it (depending on FNDECL_CODE). */
rtx
-expand_builtin_return_addr (fndecl_code, count, tem)
- enum built_in_function fndecl_code;
- int count;
- rtx tem;
+expand_builtin_return_addr (enum built_in_function fndecl_code, int count,
+ rtx tem)
{
int i;
handling code. */
void
-expand_builtin_setjmp_setup (buf_addr, receiver_label)
- rtx buf_addr;
- rtx receiver_label;
+expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label)
{
enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
rtx stack_save;
if (setjmp_alias_set == -1)
setjmp_alias_set = new_alias_set ();
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (buf_addr) != Pmode)
- buf_addr = convert_memory_address (Pmode, buf_addr);
-#endif
+ buf_addr = convert_memory_address (Pmode, buf_addr);
buf_addr = force_reg (Pmode, force_operand (buf_addr, NULL_RTX));
the buffer and use the rest of it for the stack save area, which
is machine-dependent. */
-#ifndef BUILTIN_SETJMP_FRAME_VALUE
-#define BUILTIN_SETJMP_FRAME_VALUE virtual_stack_vars_rtx
-#endif
-
mem = gen_rtx_MEM (Pmode, buf_addr);
set_mem_alias_set (mem, setjmp_alias_set);
- emit_move_insn (mem, BUILTIN_SETJMP_FRAME_VALUE);
+ emit_move_insn (mem, targetm.builtin_setjmp_frame_value ());
mem = gen_rtx_MEM (Pmode, plus_constant (buf_addr, GET_MODE_SIZE (Pmode))),
set_mem_alias_set (mem, setjmp_alias_set);
This is used directly by sjlj exception handling code. */
void
-expand_builtin_setjmp_receiver (receiver_label)
- rtx receiver_label ATTRIBUTE_UNUSED;
+expand_builtin_setjmp_receiver (rtx receiver_label ATTRIBUTE_UNUSED)
{
/* Clobber the FP when we get here, so we have to make sure it's
marked as used by this function. */
them. */
static rtx
-expand_builtin_setjmp (arglist, target)
- tree arglist;
- rtx target;
+expand_builtin_setjmp (tree arglist, rtx target)
{
rtx buf_addr, next_lab, cont_lab;
expand_builtin_setjmp_setup (buf_addr, next_lab);
- /* Set TARGET to zero and branch to the continue label. */
+ /* Set TARGET to zero and branch to the continue label. Use emit_jump to
+ ensure that pending stack adjustments are flushed. */
emit_move_insn (target, const0_rtx);
- emit_jump_insn (gen_jump (cont_lab));
- emit_barrier ();
+ emit_jump (cont_lab);
+
emit_label (next_lab);
expand_builtin_setjmp_receiver (next_lab);
them. */
void
-expand_builtin_longjmp (buf_addr, value)
- rtx buf_addr, value;
+expand_builtin_longjmp (rtx buf_addr, rtx value)
{
rtx fp, lab, stack, insn, last;
enum machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
if (setjmp_alias_set == -1)
setjmp_alias_set = new_alias_set ();
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (buf_addr) != Pmode)
- buf_addr = convert_memory_address (Pmode, buf_addr);
-#endif
+ buf_addr = convert_memory_address (Pmode, buf_addr);
buf_addr = force_reg (Pmode, buf_addr);
{
lab = copy_to_reg (lab);
+ emit_insn (gen_rtx_CLOBBER (VOIDmode,
+ gen_rtx_MEM (BLKmode,
+ gen_rtx_SCRATCH (VOIDmode))));
+ emit_insn (gen_rtx_CLOBBER (VOIDmode,
+ gen_rtx_MEM (BLKmode,
+ hard_frame_pointer_rtx)));
+
emit_move_insn (hard_frame_pointer_rtx, fp);
emit_stack_restore (SAVE_NONLOCAL, stack, NULL_RTX);
effects. */
static void
-expand_builtin_prefetch (arglist)
- tree arglist;
+expand_builtin_prefetch (tree arglist)
{
tree arg0, arg1, arg2;
rtx op0, op1, op2;
if ((! (*insn_data[(int) CODE_FOR_prefetch].operand[0].predicate)
(op0,
insn_data[(int) CODE_FOR_prefetch].operand[0].mode))
- || (GET_MODE(op0) != Pmode))
+ || (GET_MODE (op0) != Pmode))
{
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE(op0) != Pmode)
- op0 = convert_memory_address (Pmode, op0);
-#endif
+ op0 = convert_memory_address (Pmode, op0);
op0 = force_reg (Pmode, op0);
}
emit_insn (gen_prefetch (op0, op1, op2));
to be used to be used in a string instruction (cmpstrsi, movstrsi, ..). */
static rtx
-get_memory_rtx (exp)
- tree exp;
+get_memory_rtx (tree exp)
{
rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_SUM);
rtx mem;
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (addr) != Pmode)
- addr = convert_memory_address (Pmode, addr);
-#endif
+ addr = convert_memory_address (Pmode, addr);
mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
used for calling a function. */
static int apply_args_reg_offset[FIRST_PSEUDO_REGISTER];
-/* Return the offset of register REGNO into the block returned by
- __builtin_apply_args. This is not declared static, since it is
- needed in objc-act.c. */
-
-int
-apply_args_register_offset (regno)
- int regno;
-{
- apply_args_size ();
-
- /* Arguments are always put in outgoing registers (in the argument
- block) if such make sense. */
-#ifdef OUTGOING_REGNO
- regno = OUTGOING_REGNO (regno);
-#endif
- return apply_args_reg_offset[regno];
-}
-
/* Return the size required for the block returned by __builtin_apply_args,
and initialize apply_args_mode. */
static int
-apply_args_size ()
+apply_args_size (void)
{
static int size = -1;
int align;
/* The second value is the structure value address unless this is
passed as an "invisible" first argument. */
- if (struct_value_rtx)
+ if (targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0))
size += GET_MODE_SIZE (Pmode);
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (FUNCTION_ARG_REGNO_P (regno))
{
- /* Search for the proper mode for copying this register's
- value. I'm not sure this is right, but it works so far. */
- enum machine_mode best_mode = VOIDmode;
-
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
- mode != VOIDmode;
- mode = GET_MODE_WIDER_MODE (mode))
- if (HARD_REGNO_MODE_OK (regno, mode)
- && HARD_REGNO_NREGS (regno, mode) == 1)
- best_mode = mode;
-
- if (best_mode == VOIDmode)
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
- mode != VOIDmode;
- mode = GET_MODE_WIDER_MODE (mode))
- if (HARD_REGNO_MODE_OK (regno, mode)
- && have_insn_for (SET, mode))
- best_mode = mode;
-
- if (best_mode == VOIDmode)
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_FLOAT);
- mode != VOIDmode;
- mode = GET_MODE_WIDER_MODE (mode))
- if (HARD_REGNO_MODE_OK (regno, mode)
- && have_insn_for (SET, mode))
- best_mode = mode;
-
- if (best_mode == VOIDmode)
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_INT);
- mode != VOIDmode;
- mode = GET_MODE_WIDER_MODE (mode))
- if (HARD_REGNO_MODE_OK (regno, mode)
- && have_insn_for (SET, mode))
- best_mode = mode;
-
- mode = best_mode;
+ mode = reg_raw_mode[regno];
+
if (mode == VOIDmode)
abort ();
and initialize apply_result_mode. */
static int
-apply_result_size ()
+apply_result_size (void)
{
static int size = -1;
int align, regno;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (FUNCTION_VALUE_REGNO_P (regno))
{
- /* Search for the proper mode for copying this register's
- value. I'm not sure this is right, but it works so far. */
- enum machine_mode best_mode = VOIDmode;
-
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
- mode != TImode;
- mode = GET_MODE_WIDER_MODE (mode))
- if (HARD_REGNO_MODE_OK (regno, mode))
- best_mode = mode;
-
- if (best_mode == VOIDmode)
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
- mode != VOIDmode;
- mode = GET_MODE_WIDER_MODE (mode))
- if (HARD_REGNO_MODE_OK (regno, mode)
- && have_insn_for (SET, mode))
- best_mode = mode;
-
- if (best_mode == VOIDmode)
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_FLOAT);
- mode != VOIDmode;
- mode = GET_MODE_WIDER_MODE (mode))
- if (HARD_REGNO_MODE_OK (regno, mode)
- && have_insn_for (SET, mode))
- best_mode = mode;
-
- if (best_mode == VOIDmode)
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_INT);
- mode != VOIDmode;
- mode = GET_MODE_WIDER_MODE (mode))
- if (HARD_REGNO_MODE_OK (regno, mode)
- && have_insn_for (SET, mode))
- best_mode = mode;
-
- mode = best_mode;
+ mode = reg_raw_mode[regno];
+
if (mode == VOIDmode)
abort ();
restore the values. */
static rtx
-result_vector (savep, result)
- int savep;
- rtx result;
+result_vector (int savep, rtx result)
{
int regno, size, align, nelts;
enum machine_mode mode;
rtx reg, mem;
- rtx *savevec = (rtx *) alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
+ rtx *savevec = alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
size = nelts = 0;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
arguments as were passed to the current function. */
static rtx
-expand_builtin_apply_args_1 ()
+expand_builtin_apply_args_1 (void)
{
- rtx registers;
+ rtx registers, tem;
int size, align, regno;
enum machine_mode mode;
+ rtx struct_incoming_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 1);
/* Create a block where the arg-pointer, structure value address,
and argument registers can be saved. */
/* Walk past the arg-pointer and structure value address. */
size = GET_MODE_SIZE (Pmode);
- if (struct_value_rtx)
+ if (targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0))
size += GET_MODE_SIZE (Pmode);
/* Save each register used in calling a function to the block. */
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if ((mode = apply_args_mode[regno]) != VOIDmode)
{
- rtx tem;
-
align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
if (size % align != 0)
size = CEIL (size, align) * align;
}
/* Save the arg pointer to the block. */
- emit_move_insn (adjust_address (registers, Pmode, 0),
- copy_to_reg (virtual_incoming_args_rtx));
+ tem = copy_to_reg (virtual_incoming_args_rtx);
+#ifdef STACK_GROWS_DOWNWARD
+ /* We need the pointer as the caller actually passed them to us, not
+ as we might have pretended they were passed. Make sure it's a valid
+ operand, as emit_move_insn isn't expected to handle a PLUS. */
+ tem
+ = force_operand (plus_constant (tem, current_function_pretend_args_size),
+ NULL_RTX);
+#endif
+ emit_move_insn (adjust_address (registers, Pmode, 0), tem);
+
size = GET_MODE_SIZE (Pmode);
/* Save the structure value address unless this is passed as an
"invisible" first argument. */
- if (struct_value_incoming_rtx)
+ if (struct_incoming_value)
{
emit_move_insn (adjust_address (registers, Pmode, size),
- copy_to_reg (struct_value_incoming_rtx));
+ copy_to_reg (struct_incoming_value));
size += GET_MODE_SIZE (Pmode);
}
saved. */
static rtx
-expand_builtin_apply_args ()
+expand_builtin_apply_args (void)
{
/* Don't do __builtin_apply_args more than once in a function.
Save the result of the first call and reuse it. */
untyped return of whatever value was returned by the given function. */
static rtx
-expand_builtin_apply (function, arguments, argsize)
- rtx function, arguments, argsize;
+expand_builtin_apply (rtx function, rtx arguments, rtx argsize)
{
int size, align, regno;
enum machine_mode mode;
rtx incoming_args, result, reg, dest, src, call_insn;
rtx old_stack_level = 0;
rtx call_fusage = 0;
+ rtx struct_value = targetm.calls.struct_value_rtx (cfun ? TREE_TYPE (cfun->decl) : 0, 0);
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (arguments) != Pmode)
- arguments = convert_memory_address (Pmode, arguments);
-#endif
+ arguments = convert_memory_address (Pmode, arguments);
/* Create a block where the return registers can be saved. */
result = assign_stack_local (BLKmode, apply_result_size (), -1);
do_pending_stack_adjust ();
NO_DEFER_POP;
- /* Save the stack with nonlocal if available */
+ /* Save the stack with nonlocal if available. */
#ifdef HAVE_save_stack_nonlocal
if (HAVE_save_stack_nonlocal)
emit_stack_save (SAVE_NONLOCAL, &old_stack_level, NULL_RTX);
#endif
emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
- /* Push a block of memory onto the stack to store the memory arguments.
- Save the address in a register, and copy the memory arguments. ??? I
- haven't figured out how the calling convention macros effect this,
- but it's likely that the source and/or destination addresses in
- the block copy will need updating in machine specific ways. */
- dest = allocate_dynamic_stack_space (argsize, 0, BITS_PER_UNIT);
+ /* Allocate a block of memory onto the stack and copy the memory
+ arguments to the outgoing arguments address. */
+ allocate_dynamic_stack_space (argsize, 0, BITS_PER_UNIT);
+ dest = virtual_outgoing_args_rtx;
+#ifndef STACK_GROWS_DOWNWARD
+ if (GET_CODE (argsize) == CONST_INT)
+ dest = plus_constant (dest, -INTVAL (argsize));
+ else
+ dest = gen_rtx_PLUS (Pmode, dest, negate_rtx (Pmode, argsize));
+#endif
dest = gen_rtx_MEM (BLKmode, dest);
set_mem_align (dest, PARM_BOUNDARY);
src = gen_rtx_MEM (BLKmode, incoming_args);
/* Walk past the arg-pointer and structure value address. */
size = GET_MODE_SIZE (Pmode);
- if (struct_value_rtx)
+ if (struct_value)
size += GET_MODE_SIZE (Pmode);
/* Restore each of the registers previously saved. Make USE insns
/* Restore the structure value address unless this is passed as an
"invisible" first argument. */
size = GET_MODE_SIZE (Pmode);
- if (struct_value_rtx)
+ if (struct_value)
{
rtx value = gen_reg_rtx (Pmode);
emit_move_insn (value, adjust_address (arguments, Pmode, size));
- emit_move_insn (struct_value_rtx, value);
- if (GET_CODE (struct_value_rtx) == REG)
- use_reg (&call_fusage, struct_value_rtx);
+ emit_move_insn (struct_value, value);
+ if (GET_CODE (struct_value) == REG)
+ use_reg (&call_fusage, struct_value);
size += GET_MODE_SIZE (Pmode);
}
OK_DEFER_POP;
/* Return the address of the result block. */
- return copy_addr_to_reg (XEXP (result, 0));
+ result = copy_addr_to_reg (XEXP (result, 0));
+ return convert_memory_address (ptr_mode, result);
}
/* Perform an untyped return. */
static void
-expand_builtin_return (result)
- rtx result;
+expand_builtin_return (rtx result)
{
int size, align, regno;
enum machine_mode mode;
rtx reg;
rtx call_fusage = 0;
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (result) != Pmode)
- result = convert_memory_address (Pmode, result);
-#endif
+ result = convert_memory_address (Pmode, result);
apply_result_size ();
result = gen_rtx_MEM (BLKmode, result);
/* Return whatever values was restored by jumping directly to the end
of the function. */
- expand_null_return ();
+ expand_naked_return ();
}
/* Used by expand_builtin_classify_type and fold_builtin_classify_type. */
static enum type_class
-type_to_class (type)
- tree type;
+type_to_class (tree type)
{
switch (TREE_CODE (type))
{
ARGLIST. */
static rtx
-expand_builtin_classify_type (arglist)
- tree arglist;
+expand_builtin_classify_type (tree arglist)
{
if (arglist != 0)
return GEN_INT (type_to_class (TREE_TYPE (TREE_VALUE (arglist))));
/* Expand expression EXP, which is a call to __builtin_constant_p. */
static rtx
-expand_builtin_constant_p (arglist, target_mode)
- tree arglist;
- enum machine_mode target_mode;
+expand_builtin_constant_p (tree arglist, enum machine_mode target_mode)
{
rtx tmp;
/* We have taken care of the easy cases during constant folding. This
case is not obvious, so emit (constant_p_rtx (ARGLIST)) and let CSE
- get a chance to see if it can deduce whether ARGLIST is constant. */
+ get a chance to see if it can deduce whether ARGLIST is constant.
+ If CSE isn't going to run, of course, don't bother waiting. */
+
+ if (cse_not_expected)
+ return const0_rtx;
current_function_calls_constant_p = 1;
return tmp;
}
-/* Return mathematic function equivalent to FN but operating directly on TYPE,
- if available. */
+/* This helper macro, meant to be used in mathfn_built_in below,
+ determines which among a set of three builtin math functions is
+ appropriate for a given type mode. The `F' and `L' cases are
+ automatically generated from the `double' case. */
+#define CASE_MATHFN(BUILT_IN_MATHFN) \
+ case BUILT_IN_MATHFN: case BUILT_IN_MATHFN##F: case BUILT_IN_MATHFN##L: \
+ fcode = BUILT_IN_MATHFN; fcodef = BUILT_IN_MATHFN##F ; \
+ fcodel = BUILT_IN_MATHFN##L ; break;
+
+/* Return mathematic function equivalent to FN but operating directly
+ on TYPE, if available. If we can't do the conversion, return zero. */
tree
-mathfn_built_in (type, fn)
- tree type;
- enum built_in_function fn;
+mathfn_built_in (tree type, enum built_in_function fn)
{
- enum built_in_function fcode = NOT_BUILT_IN;
- if (TYPE_MODE (type) == TYPE_MODE (double_type_node))
- switch (fn)
- {
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
- fcode = BUILT_IN_SQRT;
- break;
- case BUILT_IN_SIN:
- case BUILT_IN_SINF:
- case BUILT_IN_SINL:
- fcode = BUILT_IN_SIN;
- break;
- case BUILT_IN_COS:
- case BUILT_IN_COSF:
- case BUILT_IN_COSL:
- fcode = BUILT_IN_COS;
- break;
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
- fcode = BUILT_IN_EXP;
- break;
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
- fcode = BUILT_IN_LOG;
- break;
- case BUILT_IN_TAN:
- case BUILT_IN_TANF:
- case BUILT_IN_TANL:
- fcode = BUILT_IN_TAN;
- break;
- case BUILT_IN_ATAN:
- case BUILT_IN_ATANF:
- case BUILT_IN_ATANL:
- fcode = BUILT_IN_ATAN;
- break;
- case BUILT_IN_FLOOR:
- case BUILT_IN_FLOORF:
- case BUILT_IN_FLOORL:
- fcode = BUILT_IN_FLOOR;
- break;
- case BUILT_IN_CEIL:
- case BUILT_IN_CEILF:
- case BUILT_IN_CEILL:
- fcode = BUILT_IN_CEIL;
- break;
- case BUILT_IN_TRUNC:
- case BUILT_IN_TRUNCF:
- case BUILT_IN_TRUNCL:
- fcode = BUILT_IN_TRUNC;
- break;
- case BUILT_IN_ROUND:
- case BUILT_IN_ROUNDF:
- case BUILT_IN_ROUNDL:
- fcode = BUILT_IN_ROUND;
- break;
- case BUILT_IN_NEARBYINT:
- case BUILT_IN_NEARBYINTF:
- case BUILT_IN_NEARBYINTL:
- fcode = BUILT_IN_NEARBYINT;
- break;
- default:
- abort ();
- }
- else if (TYPE_MODE (type) == TYPE_MODE (float_type_node))
- switch (fn)
- {
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
- fcode = BUILT_IN_SQRTF;
- break;
- case BUILT_IN_SIN:
- case BUILT_IN_SINF:
- case BUILT_IN_SINL:
- fcode = BUILT_IN_SINF;
- break;
- case BUILT_IN_COS:
- case BUILT_IN_COSF:
- case BUILT_IN_COSL:
- fcode = BUILT_IN_COSF;
- break;
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
- fcode = BUILT_IN_EXPF;
- break;
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
- fcode = BUILT_IN_LOGF;
- break;
- case BUILT_IN_TAN:
- case BUILT_IN_TANF:
- case BUILT_IN_TANL:
- fcode = BUILT_IN_TANF;
- break;
- case BUILT_IN_ATAN:
- case BUILT_IN_ATANF:
- case BUILT_IN_ATANL:
- fcode = BUILT_IN_ATANF;
- break;
- case BUILT_IN_FLOOR:
- case BUILT_IN_FLOORF:
- case BUILT_IN_FLOORL:
- fcode = BUILT_IN_FLOORF;
- break;
- case BUILT_IN_CEIL:
- case BUILT_IN_CEILF:
- case BUILT_IN_CEILL:
- fcode = BUILT_IN_CEILF;
- break;
- case BUILT_IN_TRUNC:
- case BUILT_IN_TRUNCF:
- case BUILT_IN_TRUNCL:
- fcode = BUILT_IN_TRUNCF;
- break;
- case BUILT_IN_ROUND:
- case BUILT_IN_ROUNDF:
- case BUILT_IN_ROUNDL:
- fcode = BUILT_IN_ROUNDF;
- break;
- case BUILT_IN_NEARBYINT:
- case BUILT_IN_NEARBYINTF:
- case BUILT_IN_NEARBYINTL:
- fcode = BUILT_IN_NEARBYINTF;
- break;
- default:
- abort ();
- }
- else if (TYPE_MODE (type) == TYPE_MODE (long_double_type_node))
- switch (fn)
- {
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
- fcode = BUILT_IN_SQRTL;
- break;
- case BUILT_IN_SIN:
- case BUILT_IN_SINF:
- case BUILT_IN_SINL:
- fcode = BUILT_IN_SINL;
- break;
- case BUILT_IN_COS:
- case BUILT_IN_COSF:
- case BUILT_IN_COSL:
- fcode = BUILT_IN_COSL;
- break;
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
- fcode = BUILT_IN_EXPL;
- break;
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
- fcode = BUILT_IN_LOGL;
- break;
- case BUILT_IN_TAN:
- case BUILT_IN_TANF:
- case BUILT_IN_TANL:
- fcode = BUILT_IN_TANL;
- break;
- case BUILT_IN_ATAN:
- case BUILT_IN_ATANF:
- case BUILT_IN_ATANL:
- fcode = BUILT_IN_ATANL;
- break;
- case BUILT_IN_FLOOR:
- case BUILT_IN_FLOORF:
- case BUILT_IN_FLOORL:
- fcode = BUILT_IN_FLOORL;
- break;
- case BUILT_IN_CEIL:
- case BUILT_IN_CEILF:
- case BUILT_IN_CEILL:
- fcode = BUILT_IN_CEILL;
- break;
- case BUILT_IN_TRUNC:
- case BUILT_IN_TRUNCF:
- case BUILT_IN_TRUNCL:
- fcode = BUILT_IN_TRUNCL;
- break;
- case BUILT_IN_ROUND:
- case BUILT_IN_ROUNDF:
- case BUILT_IN_ROUNDL:
- fcode = BUILT_IN_ROUNDL;
- break;
- case BUILT_IN_NEARBYINT:
- case BUILT_IN_NEARBYINTF:
- case BUILT_IN_NEARBYINTL:
- fcode = BUILT_IN_NEARBYINTL;
- break;
+ const enum machine_mode type_mode = TYPE_MODE (type);
+ enum built_in_function fcode, fcodef, fcodel;
+
+ switch (fn)
+ {
+ CASE_MATHFN (BUILT_IN_ACOS)
+ CASE_MATHFN (BUILT_IN_ACOSH)
+ CASE_MATHFN (BUILT_IN_ASIN)
+ CASE_MATHFN (BUILT_IN_ASINH)
+ CASE_MATHFN (BUILT_IN_ATAN)
+ CASE_MATHFN (BUILT_IN_ATAN2)
+ CASE_MATHFN (BUILT_IN_ATANH)
+ CASE_MATHFN (BUILT_IN_CBRT)
+ CASE_MATHFN (BUILT_IN_CEIL)
+ CASE_MATHFN (BUILT_IN_COPYSIGN)
+ CASE_MATHFN (BUILT_IN_COS)
+ CASE_MATHFN (BUILT_IN_COSH)
+ CASE_MATHFN (BUILT_IN_DREM)
+ CASE_MATHFN (BUILT_IN_ERF)
+ CASE_MATHFN (BUILT_IN_ERFC)
+ CASE_MATHFN (BUILT_IN_EXP)
+ CASE_MATHFN (BUILT_IN_EXP10)
+ CASE_MATHFN (BUILT_IN_EXP2)
+ CASE_MATHFN (BUILT_IN_EXPM1)
+ CASE_MATHFN (BUILT_IN_FABS)
+ CASE_MATHFN (BUILT_IN_FDIM)
+ CASE_MATHFN (BUILT_IN_FLOOR)
+ CASE_MATHFN (BUILT_IN_FMA)
+ CASE_MATHFN (BUILT_IN_FMAX)
+ CASE_MATHFN (BUILT_IN_FMIN)
+ CASE_MATHFN (BUILT_IN_FMOD)
+ CASE_MATHFN (BUILT_IN_FREXP)
+ CASE_MATHFN (BUILT_IN_GAMMA)
+ CASE_MATHFN (BUILT_IN_HUGE_VAL)
+ CASE_MATHFN (BUILT_IN_HYPOT)
+ CASE_MATHFN (BUILT_IN_ILOGB)
+ CASE_MATHFN (BUILT_IN_INF)
+ CASE_MATHFN (BUILT_IN_J0)
+ CASE_MATHFN (BUILT_IN_J1)
+ CASE_MATHFN (BUILT_IN_JN)
+ CASE_MATHFN (BUILT_IN_LDEXP)
+ CASE_MATHFN (BUILT_IN_LGAMMA)
+ CASE_MATHFN (BUILT_IN_LLRINT)
+ CASE_MATHFN (BUILT_IN_LLROUND)
+ CASE_MATHFN (BUILT_IN_LOG)
+ CASE_MATHFN (BUILT_IN_LOG10)
+ CASE_MATHFN (BUILT_IN_LOG1P)
+ CASE_MATHFN (BUILT_IN_LOG2)
+ CASE_MATHFN (BUILT_IN_LOGB)
+ CASE_MATHFN (BUILT_IN_LRINT)
+ CASE_MATHFN (BUILT_IN_LROUND)
+ CASE_MATHFN (BUILT_IN_MODF)
+ CASE_MATHFN (BUILT_IN_NAN)
+ CASE_MATHFN (BUILT_IN_NANS)
+ CASE_MATHFN (BUILT_IN_NEARBYINT)
+ CASE_MATHFN (BUILT_IN_NEXTAFTER)
+ CASE_MATHFN (BUILT_IN_NEXTTOWARD)
+ CASE_MATHFN (BUILT_IN_POW)
+ CASE_MATHFN (BUILT_IN_POW10)
+ CASE_MATHFN (BUILT_IN_REMAINDER)
+ CASE_MATHFN (BUILT_IN_REMQUO)
+ CASE_MATHFN (BUILT_IN_RINT)
+ CASE_MATHFN (BUILT_IN_ROUND)
+ CASE_MATHFN (BUILT_IN_SCALB)
+ CASE_MATHFN (BUILT_IN_SCALBLN)
+ CASE_MATHFN (BUILT_IN_SCALBN)
+ CASE_MATHFN (BUILT_IN_SIGNIFICAND)
+ CASE_MATHFN (BUILT_IN_SIN)
+ CASE_MATHFN (BUILT_IN_SINCOS)
+ CASE_MATHFN (BUILT_IN_SINH)
+ CASE_MATHFN (BUILT_IN_SQRT)
+ CASE_MATHFN (BUILT_IN_TAN)
+ CASE_MATHFN (BUILT_IN_TANH)
+ CASE_MATHFN (BUILT_IN_TGAMMA)
+ CASE_MATHFN (BUILT_IN_TRUNC)
+ CASE_MATHFN (BUILT_IN_Y0)
+ CASE_MATHFN (BUILT_IN_Y1)
+ CASE_MATHFN (BUILT_IN_YN)
+
default:
- abort ();
+ return 0;
}
- return implicit_built_in_decls[fcode];
+
+ if (type_mode == TYPE_MODE (double_type_node))
+ return implicit_built_in_decls[fcode];
+ else if (type_mode == TYPE_MODE (float_type_node))
+ return implicit_built_in_decls[fcodef];
+ else if (type_mode == TYPE_MODE (long_double_type_node))
+ return implicit_built_in_decls[fcodel];
+ else
+ return 0;
}
/* If errno must be maintained, expand the RTL to check if the result,
errno to EDOM. */
static void
-expand_errno_check (exp, target)
- tree exp;
- rtx target;
+expand_errno_check (tree exp, rtx target)
{
- rtx lab;
-
- if (flag_errno_math && HONOR_NANS (GET_MODE (target)))
- {
- lab = gen_label_rtx ();
+ rtx lab = gen_label_rtx ();
- /* Test the result; if it is NaN, set errno=EDOM because
- the argument was not in the domain. */
- emit_cmp_and_jump_insns (target, target, EQ, 0, GET_MODE (target),
- 0, lab);
+ /* Test the result; if it is NaN, set errno=EDOM because
+ the argument was not in the domain. */
+ emit_cmp_and_jump_insns (target, target, EQ, 0, GET_MODE (target),
+ 0, lab);
#ifdef TARGET_EDOM
- {
+ /* If this built-in doesn't throw an exception, set errno directly. */
+ if (TREE_NOTHROW (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
+ {
#ifdef GEN_ERRNO_RTX
- rtx errno_rtx = GEN_ERRNO_RTX;
+ rtx errno_rtx = GEN_ERRNO_RTX;
#else
- rtx errno_rtx
+ rtx errno_rtx
= gen_rtx_MEM (word_mode, gen_rtx_SYMBOL_REF (Pmode, "errno"));
#endif
-
- emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM));
- }
-#else
- /* We can't set errno=EDOM directly; let the library call do it.
- Pop the arguments right away in case the call gets deleted. */
- NO_DEFER_POP;
- expand_call (exp, target, 0);
- OK_DEFER_POP;
-#endif
-
+ emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM));
emit_label (lab);
+ return;
}
+#endif
+
+ /* We can't set errno=EDOM directly; let the library call do it.
+ Pop the arguments right away in case the call gets deleted. */
+ NO_DEFER_POP;
+ expand_call (exp, target, 0);
+ OK_DEFER_POP;
+ emit_label (lab);
}
SUBTARGET may be used as the target for computing one of EXP's operands. */
static rtx
-expand_builtin_mathfn (exp, target, subtarget)
- tree exp;
- rtx target, subtarget;
+expand_builtin_mathfn (tree exp, rtx target, rtx subtarget)
{
optab builtin_optab;
- rtx op0, insns;
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ rtx op0, insns, before_call;
+ tree fndecl = get_callee_fndecl (exp);
tree arglist = TREE_OPERAND (exp, 1);
- enum machine_mode argmode;
- bool errno_set = true;
+ enum machine_mode mode;
+ bool errno_set = false;
+ tree arg, narg;
if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
return 0;
- /* Stabilize and compute the argument. */
- if (TREE_CODE (TREE_VALUE (arglist)) != VAR_DECL
- && TREE_CODE (TREE_VALUE (arglist)) != PARM_DECL)
- {
- exp = copy_node (exp);
- TREE_OPERAND (exp, 1) = arglist;
- /* Wrap the computation of the argument in a SAVE_EXPR. That
- way, if we need to expand the argument again (as in the
- flag_errno_math case below where we cannot directly set
- errno), we will not perform side-effects more than once.
- Note that here we're mutating the original EXP as well as the
- copy; that's the right thing to do in case the original EXP
- is expanded later. */
- TREE_VALUE (arglist) = save_expr (TREE_VALUE (arglist));
- arglist = copy_node (arglist);
- }
- op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0);
-
- /* Make a suitable register to place result in. */
- target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
-
- emit_queue ();
- start_sequence ();
+ arg = TREE_VALUE (arglist);
switch (DECL_FUNCTION_CODE (fndecl))
{
case BUILT_IN_SQRT:
case BUILT_IN_SQRTF:
case BUILT_IN_SQRTL:
- builtin_optab = sqrt_optab; break;
+ errno_set = ! tree_expr_nonnegative_p (arg);
+ builtin_optab = sqrt_optab;
+ break;
case BUILT_IN_EXP:
case BUILT_IN_EXPF:
case BUILT_IN_EXPL:
- builtin_optab = exp_optab; break;
+ errno_set = true; builtin_optab = exp_optab; break;
+ case BUILT_IN_EXP10:
+ case BUILT_IN_EXP10F:
+ case BUILT_IN_EXP10L:
+ case BUILT_IN_POW10:
+ case BUILT_IN_POW10F:
+ case BUILT_IN_POW10L:
+ errno_set = true; builtin_optab = exp10_optab; break;
+ case BUILT_IN_EXP2:
+ case BUILT_IN_EXP2F:
+ case BUILT_IN_EXP2L:
+ errno_set = true; builtin_optab = exp2_optab; break;
case BUILT_IN_LOG:
case BUILT_IN_LOGF:
case BUILT_IN_LOGL:
- builtin_optab = log_optab; break;
+ errno_set = true; builtin_optab = log_optab; break;
+ case BUILT_IN_LOG10:
+ case BUILT_IN_LOG10F:
+ case BUILT_IN_LOG10L:
+ errno_set = true; builtin_optab = log10_optab; break;
+ case BUILT_IN_LOG2:
+ case BUILT_IN_LOG2F:
+ case BUILT_IN_LOG2L:
+ errno_set = true; builtin_optab = log2_optab; break;
+ case BUILT_IN_TAN:
+ case BUILT_IN_TANF:
+ case BUILT_IN_TANL:
+ builtin_optab = tan_optab; break;
+ case BUILT_IN_ATAN:
+ case BUILT_IN_ATANF:
+ case BUILT_IN_ATANL:
+ builtin_optab = atan_optab; break;
case BUILT_IN_FLOOR:
case BUILT_IN_FLOORF:
case BUILT_IN_FLOORL:
- errno_set = false ; builtin_optab = floor_optab; break;
+ builtin_optab = floor_optab; break;
case BUILT_IN_CEIL:
case BUILT_IN_CEILF:
case BUILT_IN_CEILL:
- errno_set = false ; builtin_optab = ceil_optab; break;
+ builtin_optab = ceil_optab; break;
case BUILT_IN_TRUNC:
case BUILT_IN_TRUNCF:
case BUILT_IN_TRUNCL:
- errno_set = false ; builtin_optab = trunc_optab; break;
+ builtin_optab = btrunc_optab; break;
case BUILT_IN_ROUND:
case BUILT_IN_ROUNDF:
case BUILT_IN_ROUNDL:
- errno_set = false ; builtin_optab = round_optab; break;
+ builtin_optab = round_optab; break;
case BUILT_IN_NEARBYINT:
case BUILT_IN_NEARBYINTF:
case BUILT_IN_NEARBYINTL:
- errno_set = false ; builtin_optab = nearbyint_optab; break;
+ builtin_optab = nearbyint_optab; break;
default:
abort ();
}
- /* Compute into TARGET.
- Set TARGET to wherever the result comes back. */
- argmode = TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist)));
- target = expand_unop (argmode, builtin_optab, op0, target, 0);
+ /* Make a suitable register to place result in. */
+ mode = TYPE_MODE (TREE_TYPE (exp));
- /* If we were unable to expand via the builtin, stop the
- sequence (without outputting the insns) and return 0, causing
- a call to the library function. */
- if (target == 0)
+ if (! flag_errno_math || ! HONOR_NANS (mode))
+ errno_set = false;
+
+ /* Before working hard, check whether the instruction is available. */
+ if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
{
+ target = gen_reg_rtx (mode);
+
+ /* Wrap the computation of the argument in a SAVE_EXPR, as we may
+ need to expand the argument again. This way, we will not perform
+ side-effects more the once. */
+ narg = save_expr (arg);
+ if (narg != arg)
+ {
+ arglist = build_tree_list (NULL_TREE, arg);
+ exp = build_function_call_expr (fndecl, arglist);
+ }
+
+ op0 = expand_expr (arg, subtarget, VOIDmode, 0);
+
+ emit_queue ();
+ start_sequence ();
+
+ /* Compute into TARGET.
+ Set TARGET to wherever the result comes back. */
+ target = expand_unop (mode, builtin_optab, op0, target, 0);
+
+ if (target != 0)
+ {
+ if (errno_set)
+ expand_errno_check (exp, target);
+
+ /* Output the entire sequence. */
+ insns = get_insns ();
+ end_sequence ();
+ emit_insn (insns);
+ return target;
+ }
+
+ /* 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 0;
}
- if (errno_set)
- expand_errno_check (exp, target);
+ before_call = get_last_insn ();
- /* Output the entire sequence. */
- insns = get_insns ();
- end_sequence ();
- emit_insn (insns);
+ target = expand_call (exp, target, target == const0_rtx);
+
+ /* If this is a sqrt operation and we don't care about errno, try to
+ attach a REG_EQUAL note with a SQRT rtx to the emitted libcall.
+ This allows the semantics of the libcall to be visible to the RTL
+ optimizers. */
+ if (builtin_optab == sqrt_optab && !errno_set)
+ {
+ /* Search backwards through the insns emitted by expand_call looking
+ for the instruction with the REG_RETVAL note. */
+ rtx last = get_last_insn ();
+ while (last != before_call)
+ {
+ if (find_reg_note (last, REG_RETVAL, NULL))
+ {
+ rtx note = find_reg_note (last, REG_EQUAL, NULL);
+ /* Check that the REQ_EQUAL note is an EXPR_LIST with
+ two elements, i.e. symbol_ref(sqrt) and the operand. */
+ if (note
+ && GET_CODE (note) == EXPR_LIST
+ && GET_CODE (XEXP (note, 0)) == EXPR_LIST
+ && XEXP (XEXP (note, 0), 1) != NULL_RTX
+ && XEXP (XEXP (XEXP (note, 0), 1), 1) == NULL_RTX)
+ {
+ rtx operand = XEXP (XEXP (XEXP (note, 0), 1), 0);
+ /* Check operand is a register with expected mode. */
+ if (operand
+ && GET_CODE (operand) == REG
+ && GET_MODE (operand) == mode)
+ {
+ /* Replace the REG_EQUAL note with a SQRT rtx. */
+ rtx equiv = gen_rtx_SQRT (mode, operand);
+ set_unique_reg_note (last, REG_EQUAL, equiv);
+ }
+ }
+ break;
+ }
+ last = PREV_INSN (last);
+ }
+ }
return target;
}
operands. */
static rtx
-expand_builtin_mathfn_2 (exp, target, subtarget)
- tree exp;
- rtx target, subtarget;
+expand_builtin_mathfn_2 (tree exp, rtx target, rtx subtarget)
{
optab builtin_optab;
rtx op0, op1, insns;
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree fndecl = get_callee_fndecl (exp);
tree arglist = TREE_OPERAND (exp, 1);
- tree arg0, arg1;
- enum machine_mode argmode;
+ tree arg0, arg1, temp, narg;
+ enum machine_mode mode;
bool errno_set = true;
bool stable = true;
arg0 = TREE_VALUE (arglist);
arg1 = TREE_VALUE (TREE_CHAIN (arglist));
- /* Stabilize the arguments. */
- if (TREE_CODE (arg0) != VAR_DECL && TREE_CODE (arg0) != PARM_DECL)
+ switch (DECL_FUNCTION_CODE (fndecl))
{
- arg0 = save_expr (arg0);
- TREE_VALUE (arglist) = arg0;
- stable = false;
+ case BUILT_IN_POW:
+ case BUILT_IN_POWF:
+ case BUILT_IN_POWL:
+ builtin_optab = pow_optab; break;
+ case BUILT_IN_ATAN2:
+ case BUILT_IN_ATAN2F:
+ case BUILT_IN_ATAN2L:
+ builtin_optab = atan2_optab; break;
+ default:
+ abort ();
}
- if (TREE_CODE (arg1) != VAR_DECL && TREE_CODE (arg1) != PARM_DECL)
+
+ /* Make a suitable register to place result in. */
+ mode = TYPE_MODE (TREE_TYPE (exp));
+
+ /* Before working hard, check whether the instruction is available. */
+ if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+ return 0;
+
+ target = gen_reg_rtx (mode);
+
+ if (! flag_errno_math || ! HONOR_NANS (mode))
+ errno_set = false;
+
+ /* Alway stabilize the argument list. */
+ narg = save_expr (arg1);
+ if (narg != arg1)
{
- arg1 = save_expr (arg1);
- TREE_VALUE (TREE_CHAIN (arglist)) = arg1;
+ temp = build_tree_list (NULL_TREE, narg);
stable = false;
}
+ else
+ temp = TREE_CHAIN (arglist);
- if (! stable)
+ narg = save_expr (arg0);
+ if (narg != arg0)
{
- exp = copy_node (exp);
- arglist = tree_cons (NULL_TREE, arg0,
- build_tree_list (NULL_TREE, arg1));
- TREE_OPERAND (exp, 1) = arglist;
+ arglist = tree_cons (NULL_TREE, narg, temp);
+ stable = false;
}
+ else if (! stable)
+ arglist = tree_cons (NULL_TREE, arg0, temp);
+
+ if (! stable)
+ exp = build_function_call_expr (fndecl, arglist);
op0 = expand_expr (arg0, subtarget, VOIDmode, 0);
op1 = expand_expr (arg1, 0, VOIDmode, 0);
- /* Make a suitable register to place result in. */
- target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
-
emit_queue ();
start_sequence ();
- switch (DECL_FUNCTION_CODE (fndecl))
- {
- case BUILT_IN_POW:
- case BUILT_IN_POWF:
- case BUILT_IN_POWL:
- builtin_optab = pow_optab; break;
- case BUILT_IN_ATAN2:
- case BUILT_IN_ATAN2F:
- case BUILT_IN_ATAN2L:
- builtin_optab = atan2_optab; break;
- default:
- abort ();
- }
-
/* Compute into TARGET.
Set TARGET to wherever the result comes back. */
- argmode = TYPE_MODE (TREE_TYPE (arg0));
- target = expand_binop (argmode, builtin_optab, op0, op1,
+ target = expand_binop (mode, builtin_optab, op0, op1,
target, 0, OPTAB_DIRECT);
- /* If we were unable to expand via the builtin, stop the
- sequence (without outputting the insns) and return 0, causing
- a call to the library function. */
+ /* 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)
{
end_sequence ();
- return 0;
+ return expand_call (exp, target, target == const0_rtx);
}
if (errno_set)
return target;
}
-/* Expand expression EXP which is a call to the strlen builtin. Return 0
- if we failed the caller should emit a normal call, otherwise
- try to get the result in TARGET, if convenient. */
+/* To evaluate powi(x,n), the floating point value x raised to the
+ constant integer exponent n, we use a hybrid algorithm that
+ combines the "window method" with look-up tables. For an
+ introduction to exponentiation algorithms and "addition chains",
+ see section 4.6.3, "Evaluation of Powers" of Donald E. Knuth,
+ "Seminumerical Algorithms", Vol. 2, "The Art of Computer Programming",
+ 3rd Edition, 1998, and Daniel M. Gordon, "A Survey of Fast Exponentiation
+ Methods", Journal of Algorithms, Vol. 27, pp. 129-146, 1998. */
+
+/* Provide a default value for POWI_MAX_MULTS, the maximum number of
+ multiplications to inline before calling the system library's pow
+ function. powi(x,n) requires at worst 2*bits(n)-2 multiplications,
+ so this default never requires calling pow, powf or powl. */
+
+#ifndef POWI_MAX_MULTS
+#define POWI_MAX_MULTS (2*HOST_BITS_PER_WIDE_INT-2)
+#endif
-static rtx
-expand_builtin_strlen (arglist, target, target_mode)
- tree arglist;
- rtx target;
- enum machine_mode target_mode;
+/* The size of the "optimal power tree" lookup table. All
+ exponents less than this value are simply looked up in the
+ powi_table below. This threshold is also used to size the
+ cache of pseudo registers that hold intermediate results. */
+#define POWI_TABLE_SIZE 256
+
+/* The size, in bits of the window, used in the "window method"
+ exponentiation algorithm. This is equivalent to a radix of
+ (1<<POWI_WINDOW_SIZE) in the corresponding "m-ary method". */
+#define POWI_WINDOW_SIZE 3
+
+/* The following table is an efficient representation of an
+ "optimal power tree". For each value, i, the corresponding
+ value, j, in the table states than an optimal evaluation
+ sequence for calculating pow(x,i) can be found by evaluating
+ pow(x,j)*pow(x,i-j). An optimal power tree for the first
+ 100 integers is given in Knuth's "Seminumerical algorithms". */
+
+static const unsigned char powi_table[POWI_TABLE_SIZE] =
+ {
+ 0, 1, 1, 2, 2, 3, 3, 4, /* 0 - 7 */
+ 4, 6, 5, 6, 6, 10, 7, 9, /* 8 - 15 */
+ 8, 16, 9, 16, 10, 12, 11, 13, /* 16 - 23 */
+ 12, 17, 13, 18, 14, 24, 15, 26, /* 24 - 31 */
+ 16, 17, 17, 19, 18, 33, 19, 26, /* 32 - 39 */
+ 20, 25, 21, 40, 22, 27, 23, 44, /* 40 - 47 */
+ 24, 32, 25, 34, 26, 29, 27, 44, /* 48 - 55 */
+ 28, 31, 29, 34, 30, 60, 31, 36, /* 56 - 63 */
+ 32, 64, 33, 34, 34, 46, 35, 37, /* 64 - 71 */
+ 36, 65, 37, 50, 38, 48, 39, 69, /* 72 - 79 */
+ 40, 49, 41, 43, 42, 51, 43, 58, /* 80 - 87 */
+ 44, 64, 45, 47, 46, 59, 47, 76, /* 88 - 95 */
+ 48, 65, 49, 66, 50, 67, 51, 66, /* 96 - 103 */
+ 52, 70, 53, 74, 54, 104, 55, 74, /* 104 - 111 */
+ 56, 64, 57, 69, 58, 78, 59, 68, /* 112 - 119 */
+ 60, 61, 61, 80, 62, 75, 63, 68, /* 120 - 127 */
+ 64, 65, 65, 128, 66, 129, 67, 90, /* 128 - 135 */
+ 68, 73, 69, 131, 70, 94, 71, 88, /* 136 - 143 */
+ 72, 128, 73, 98, 74, 132, 75, 121, /* 144 - 151 */
+ 76, 102, 77, 124, 78, 132, 79, 106, /* 152 - 159 */
+ 80, 97, 81, 160, 82, 99, 83, 134, /* 160 - 167 */
+ 84, 86, 85, 95, 86, 160, 87, 100, /* 168 - 175 */
+ 88, 113, 89, 98, 90, 107, 91, 122, /* 176 - 183 */
+ 92, 111, 93, 102, 94, 126, 95, 150, /* 184 - 191 */
+ 96, 128, 97, 130, 98, 133, 99, 195, /* 192 - 199 */
+ 100, 128, 101, 123, 102, 164, 103, 138, /* 200 - 207 */
+ 104, 145, 105, 146, 106, 109, 107, 149, /* 208 - 215 */
+ 108, 200, 109, 146, 110, 170, 111, 157, /* 216 - 223 */
+ 112, 128, 113, 130, 114, 182, 115, 132, /* 224 - 231 */
+ 116, 200, 117, 132, 118, 158, 119, 206, /* 232 - 239 */
+ 120, 240, 121, 162, 122, 147, 123, 152, /* 240 - 247 */
+ 124, 166, 125, 214, 126, 138, 127, 153, /* 248 - 255 */
+ };
+
+
+/* Return the number of multiplications required to calculate
+ powi(x,n) where n is less than POWI_TABLE_SIZE. This is a
+ subroutine of powi_cost. CACHE is an array indicating
+ which exponents have already been calculated. */
+
+static int
+powi_lookup_cost (unsigned HOST_WIDE_INT n, bool *cache)
{
- if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
+ /* If we've already calculated this exponent, then this evaluation
+ doesn't require any additional multiplications. */
+ if (cache[n])
return 0;
- else
- {
- rtx pat;
- tree len, src = TREE_VALUE (arglist);
- rtx result, src_reg, char_rtx, before_strlen;
- enum machine_mode insn_mode = target_mode, char_mode;
- enum insn_code icode = CODE_FOR_nothing;
- int align;
- /* If the length can be computed at compile-time, return it. */
- len = c_strlen (src);
- if (len)
- return expand_expr (len, target, target_mode, EXPAND_NORMAL);
+ cache[n] = true;
+ return powi_lookup_cost (n - powi_table[n], cache)
+ + powi_lookup_cost (powi_table[n], cache) + 1;
+}
- align = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+/* Return the number of multiplications required to calculate
+ powi(x,n) for an arbitrary x, given the exponent N. This
+ function needs to be kept in sync with expand_powi below. */
- /* If SRC is not a pointer type, don't do this operation inline. */
- if (align == 0)
- return 0;
+static int
+powi_cost (HOST_WIDE_INT n)
+{
+ bool cache[POWI_TABLE_SIZE];
+ unsigned HOST_WIDE_INT digit;
+ unsigned HOST_WIDE_INT val;
+ int result;
- /* Bail out if we can't compute strlen in the right mode. */
- while (insn_mode != VOIDmode)
- {
- icode = strlen_optab->handlers[(int) insn_mode].insn_code;
- if (icode != CODE_FOR_nothing)
- break;
+ if (n == 0)
+ return 0;
- insn_mode = GET_MODE_WIDER_MODE (insn_mode);
+ /* Ignore the reciprocal when calculating the cost. */
+ val = (n < 0) ? -n : n;
+
+ /* Initialize the exponent cache. */
+ memset (cache, 0, POWI_TABLE_SIZE * sizeof (bool));
+ cache[1] = true;
+
+ result = 0;
+
+ while (val >= POWI_TABLE_SIZE)
+ {
+ if (val & 1)
+ {
+ digit = val & ((1 << POWI_WINDOW_SIZE) - 1);
+ result += powi_lookup_cost (digit, cache)
+ + POWI_WINDOW_SIZE + 1;
+ val >>= POWI_WINDOW_SIZE;
}
- if (insn_mode == VOIDmode)
- return 0;
+ else
+ {
+ val >>= 1;
+ result++;
+ }
+ }
- /* Make a place to write the result of the instruction. */
- result = target;
- if (! (result != 0
- && GET_CODE (result) == REG
- && GET_MODE (result) == insn_mode
- && REGNO (result) >= FIRST_PSEUDO_REGISTER))
- result = gen_reg_rtx (insn_mode);
+ return result + powi_lookup_cost (val, cache);
+}
- /* Make a place to hold the source address. We will not expand
- the actual source until we are sure that the expansion will
- not fail -- there are trees that cannot be expanded twice. */
- src_reg = gen_reg_rtx (Pmode);
+/* Recursive subroutine of expand_powi. This function takes the array,
+ CACHE, of already calculated exponents and an exponent N and returns
+ an RTX that corresponds to CACHE[1]**N, as calculated in mode MODE. */
- /* Mark the beginning of the strlen sequence so we can emit the
- source operand later. */
- before_strlen = get_last_insn ();
+static rtx
+expand_powi_1 (enum machine_mode mode, unsigned HOST_WIDE_INT n, rtx *cache)
+{
+ unsigned HOST_WIDE_INT digit;
+ rtx target, result;
+ rtx op0, op1;
- char_rtx = const0_rtx;
+ if (n < POWI_TABLE_SIZE)
+ {
+ if (cache[n])
+ return cache[n];
+
+ target = gen_reg_rtx (mode);
+ cache[n] = target;
+
+ op0 = expand_powi_1 (mode, n - powi_table[n], cache);
+ op1 = expand_powi_1 (mode, powi_table[n], cache);
+ }
+ else if (n & 1)
+ {
+ target = gen_reg_rtx (mode);
+ digit = n & ((1 << POWI_WINDOW_SIZE) - 1);
+ op0 = expand_powi_1 (mode, n - digit, cache);
+ op1 = expand_powi_1 (mode, digit, cache);
+ }
+ else
+ {
+ target = gen_reg_rtx (mode);
+ op0 = expand_powi_1 (mode, n >> 1, cache);
+ op1 = op0;
+ }
+
+ result = expand_mult (mode, op0, op1, target, 0);
+ if (result != target)
+ emit_move_insn (target, result);
+ return target;
+}
+
+/* Expand the RTL to evaluate powi(x,n) in mode MODE. X is the
+ floating point operand in mode MODE, and N is the exponent. This
+ function needs to be kept in sync with powi_cost above. */
+
+static rtx
+expand_powi (rtx x, enum machine_mode mode, HOST_WIDE_INT n)
+{
+ unsigned HOST_WIDE_INT val;
+ rtx cache[POWI_TABLE_SIZE];
+ rtx result;
+
+ if (n == 0)
+ return CONST1_RTX (mode);
+
+ val = (n < 0) ? -n : n;
+
+ memset (cache, 0, sizeof (cache));
+ cache[1] = x;
+
+ result = expand_powi_1 (mode, (n < 0) ? -n : n, cache);
+
+ /* If the original exponent was negative, reciprocate the result. */
+ if (n < 0)
+ result = expand_binop (mode, sdiv_optab, CONST1_RTX (mode),
+ result, NULL_RTX, 0, OPTAB_LIB_WIDEN);
+
+ return result;
+}
+
+/* Expand a call to the pow built-in mathematical function. Return 0 if
+ a normal call should be emitted rather than expanding the function
+ in-line. EXP is the expression that is a call to the builtin
+ function; if convenient, the result should be placed in TARGET. */
+
+static rtx
+expand_builtin_pow (tree exp, rtx target, rtx subtarget)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg0, arg1;
+
+ if (! validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ arg0 = TREE_VALUE (arglist);
+ arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+
+ if (TREE_CODE (arg1) == REAL_CST
+ && ! TREE_CONSTANT_OVERFLOW (arg1))
+ {
+ REAL_VALUE_TYPE cint;
+ REAL_VALUE_TYPE c;
+ HOST_WIDE_INT n;
+
+ c = TREE_REAL_CST (arg1);
+ n = real_to_integer (&c);
+ real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
+ if (real_identical (&c, &cint))
+ {
+ /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact.
+ Otherwise, check the number of multiplications required.
+ Note that pow never sets errno for an integer exponent. */
+ if ((n >= -1 && n <= 2)
+ || (flag_unsafe_math_optimizations
+ && ! optimize_size
+ && powi_cost (n) <= POWI_MAX_MULTS))
+ {
+ enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
+ rtx op = expand_expr (arg0, subtarget, VOIDmode, 0);
+ op = force_reg (mode, op);
+ return expand_powi (op, mode, n);
+ }
+ }
+ }
+
+ if (! flag_unsafe_math_optimizations)
+ return NULL_RTX;
+ return expand_builtin_mathfn_2 (exp, target, subtarget);
+}
+
+/* Expand expression EXP which is a call to the strlen builtin. Return 0
+ if we failed the caller should emit a normal call, otherwise
+ try to get the result in TARGET, if convenient. */
+
+static rtx
+expand_builtin_strlen (tree arglist, rtx target,
+ enum machine_mode target_mode)
+{
+ if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
+ return 0;
+ else
+ {
+ rtx pat;
+ tree len, src = TREE_VALUE (arglist);
+ rtx result, src_reg, char_rtx, before_strlen;
+ enum machine_mode insn_mode = target_mode, char_mode;
+ enum insn_code icode = CODE_FOR_nothing;
+ int align;
+
+ /* If the length can be computed at compile-time, return it. */
+ len = c_strlen (src, 0);
+ if (len)
+ return expand_expr (len, target, target_mode, EXPAND_NORMAL);
+
+ /* If the length can be computed at compile-time and is constant
+ integer, but there are side-effects in src, evaluate
+ src for side-effects, then return len.
+ E.g. x = strlen (i++ ? "xfoo" + 1 : "bar");
+ can be optimized into: i++; x = 3; */
+ len = c_strlen (src, 1);
+ if (len && TREE_CODE (len) == INTEGER_CST)
+ {
+ expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ return expand_expr (len, target, target_mode, EXPAND_NORMAL);
+ }
+
+ align = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+
+ /* If SRC is not a pointer type, don't do this operation inline. */
+ if (align == 0)
+ return 0;
+
+ /* Bail out if we can't compute strlen in the right mode. */
+ while (insn_mode != VOIDmode)
+ {
+ icode = strlen_optab->handlers[(int) insn_mode].insn_code;
+ if (icode != CODE_FOR_nothing)
+ break;
+
+ insn_mode = GET_MODE_WIDER_MODE (insn_mode);
+ }
+ if (insn_mode == VOIDmode)
+ return 0;
+
+ /* Make a place to write the result of the instruction. */
+ result = target;
+ if (! (result != 0
+ && GET_CODE (result) == REG
+ && GET_MODE (result) == insn_mode
+ && REGNO (result) >= FIRST_PSEUDO_REGISTER))
+ result = gen_reg_rtx (insn_mode);
+
+ /* Make a place to hold the source address. We will not expand
+ the actual source until we are sure that the expansion will
+ not fail -- there are trees that cannot be expanded twice. */
+ src_reg = gen_reg_rtx (Pmode);
+
+ /* Mark the beginning of the strlen sequence so we can emit the
+ source operand later. */
+ before_strlen = get_last_insn ();
+
+ char_rtx = const0_rtx;
char_mode = insn_data[(int) icode].operand[2].mode;
if (! (*insn_data[(int) icode].operand[2].predicate) (char_rtx,
char_mode))
in TARGET, if convenient (and in mode MODE if that's convenient). */
static rtx
-expand_builtin_strstr (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_strstr (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
/* Return an offset into the constant string argument. */
return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
- s1, ssize_int (r - p1))),
+ s1, convert (TREE_TYPE (s1),
+ ssize_int (r - p1)))),
target, mode, EXPAND_NORMAL);
}
in TARGET, if convenient (and in mode MODE if that's convenient). */
static rtx
-expand_builtin_strchr (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_strchr (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
/* Return an offset into the constant string argument. */
return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
- s1, ssize_int (r - p1))),
+ s1, convert (TREE_TYPE (s1),
+ ssize_int (r - p1)))),
target, mode, EXPAND_NORMAL);
}
in TARGET, if convenient (and in mode MODE if that's convenient). */
static rtx
-expand_builtin_strrchr (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_strrchr (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
/* Return an offset into the constant string argument. */
return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
- s1, ssize_int (r - p1))),
+ s1, convert (TREE_TYPE (s1),
+ ssize_int (r - p1)))),
target, mode, EXPAND_NORMAL);
}
in TARGET, if convenient (and in mode MODE if that's convenient). */
static rtx
-expand_builtin_strpbrk (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_strpbrk (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
/* Return an offset into the constant string argument. */
return expand_expr (fold (build (PLUS_EXPR, TREE_TYPE (s1),
- s1, ssize_int (r - p1))),
+ s1, convert (TREE_TYPE (s1),
+ ssize_int (r - p1)))),
target, mode, EXPAND_NORMAL);
}
constant. */
static rtx
-builtin_memcpy_read_str (data, offset, mode)
- PTR data;
- HOST_WIDE_INT offset;
- enum machine_mode mode;
+builtin_memcpy_read_str (void *data, HOST_WIDE_INT offset,
+ enum machine_mode mode)
{
const char *str = (const char *) data;
/* Expand a call to the memcpy builtin, with arguments in ARGLIST.
Return 0 if we failed, the caller should emit a normal call,
otherwise try to get the result in TARGET, if convenient (and in
- mode MODE if that's convenient). If ENDP is 0 return the
- destination pointer, if ENDP is 1 return the end pointer ala
- mempcpy, and if ENDP is 2 return the end pointer minus one ala
- stpcpy. */
+ mode MODE if that's convenient). */
static rtx
-expand_builtin_memcpy (arglist, target, mode, endp)
- tree arglist;
- rtx target;
- enum machine_mode mode;
- int endp;
+expand_builtin_memcpy (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
tree src = TREE_VALUE (TREE_CHAIN (arglist));
tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
const char *src_str;
-
unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
unsigned int dest_align
= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
return 0;
/* If the LEN parameter is zero, return DEST. */
- if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
+ if (integer_zerop (len))
{
/* Evaluate and ignore SRC in case it has side-effects. */
expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
return expand_expr (dest, target, mode, EXPAND_NORMAL);
}
+ /* If SRC and DEST are the same (and not volatile), return DEST. */
+ if (operand_equal_p (src, dest, 0))
+ {
+ /* Evaluate and ignore LEN in case it has side-effects. */
+ expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ return expand_expr (dest, target, mode, EXPAND_NORMAL);
+ }
+
/* If either SRC is not a pointer type, don't do this
operation in-line. */
if (src_align == 0)
&& GET_CODE (len_rtx) == CONST_INT
&& (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1
&& can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
- (PTR) src_str, dest_align))
+ (void *) src_str, dest_align))
{
- store_by_pieces (dest_mem, INTVAL (len_rtx),
- builtin_memcpy_read_str,
- (PTR) src_str, dest_align);
+ dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx),
+ builtin_memcpy_read_str,
+ (void *) src_str, dest_align, 0);
dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (dest_mem) != ptr_mode)
- dest_mem = convert_memory_address (ptr_mode, dest_mem);
-#endif
- if (endp)
- {
- rtx result;
- rtx delta = len_rtx;
-
- if (endp == 2)
- delta = GEN_INT (INTVAL (delta) - 1);
-
- result = simplify_gen_binary (PLUS, GET_MODE (dest_mem),
- dest_mem, delta);
- return force_operand (result, NULL_RTX);
- }
- else
- return dest_mem;
+ dest_mem = convert_memory_address (ptr_mode, dest_mem);
+ return dest_mem;
}
src_mem = get_memory_rtx (src);
if (dest_addr == 0)
{
dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (dest_addr) != ptr_mode)
- dest_addr = convert_memory_address (ptr_mode, dest_addr);
-#endif
+ dest_addr = convert_memory_address (ptr_mode, dest_addr);
}
-
- if (endp)
- {
- rtx result = force_operand (len_rtx, NULL_RTX);
-
- if (endp == 2)
- {
- result = simplify_gen_binary (MINUS, GET_MODE (dest_addr),
- result, const1_rtx);
- result = force_operand (result, NULL_RTX);
- }
-
- result = simplify_gen_binary (PLUS, GET_MODE (dest_addr),
- dest_addr, result);
- return force_operand (result, NULL_RTX);
- }
- else
- return dest_addr;
+ return dest_addr;
}
}
/* Expand a call to the mempcpy builtin, with arguments in ARGLIST.
Return 0 if we failed the caller should emit a normal call,
otherwise try to get the result in TARGET, if convenient (and in
- mode MODE if that's convenient). */
+ mode MODE if that's convenient). If ENDP is 0 return the
+ destination pointer, if ENDP is 1 return the end pointer ala
+ mempcpy, and if ENDP is 2 return the end pointer minus one ala
+ stpcpy. */
static rtx
-expand_builtin_mempcpy (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_mempcpy (tree arglist, rtx target, enum machine_mode mode,
+ int endp)
{
if (!validate_arglist (arglist,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
+ /* If return value is ignored, transform mempcpy into memcpy. */
+ else if (target == const0_rtx)
+ {
+ tree fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+
+ if (!fn)
+ return 0;
+
+ return expand_expr (build_function_call_expr (fn, arglist),
+ target, mode, EXPAND_NORMAL);
+ }
else
{
- /* If return value is ignored, transform mempcpy into memcpy. */
- if (target == const0_rtx)
+ tree dest = TREE_VALUE (arglist);
+ tree src = TREE_VALUE (TREE_CHAIN (arglist));
+ tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ const char *src_str;
+ unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
+ unsigned int dest_align
+ = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
+ rtx dest_mem, src_mem, len_rtx;
+
+ /* If DEST is not a pointer type, call the normal function. */
+ if (dest_align == 0)
+ return 0;
+
+ /* If SRC and DEST are the same (and not volatile), do nothing. */
+ if (operand_equal_p (src, dest, 0))
{
- tree fn;
- rtx ret = expand_builtin_memcpy (arglist, target, mode, /*endp=*/0);
+ tree expr;
- if (ret)
- return ret;
+ if (endp == 0)
+ {
+ /* Evaluate and ignore LEN in case it has side-effects. */
+ expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ return expand_expr (dest, target, mode, EXPAND_NORMAL);
+ }
- fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
- if (!fn)
- return 0;
+ if (endp == 2)
+ len = fold (build (MINUS_EXPR, TREE_TYPE (len), dest,
+ integer_one_node));
+ len = convert (TREE_TYPE (dest), len);
+ expr = fold (build (PLUS_EXPR, TREE_TYPE (dest), dest, len));
+ return expand_expr (expr, target, mode, EXPAND_NORMAL);
+ }
- return expand_expr (build_function_call_expr (fn, arglist),
- target, mode, EXPAND_NORMAL);
+ /* If LEN is not constant, call the normal function. */
+ if (! host_integerp (len, 1))
+ return 0;
+
+ /* If the LEN parameter is zero, return DEST. */
+ if (tree_low_cst (len, 1) == 0)
+ {
+ /* Evaluate and ignore SRC in case it has side-effects. */
+ expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ return expand_expr (dest, target, mode, EXPAND_NORMAL);
+ }
+
+ /* If either SRC is not a pointer type, don't do this
+ operation in-line. */
+ if (src_align == 0)
+ return 0;
+
+ len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+ src_str = c_getstr (src);
+
+ /* If SRC is a string constant and block move would be done
+ by pieces, we can avoid loading the string from memory
+ and only stored the computed constants. */
+ if (src_str
+ && GET_CODE (len_rtx) == CONST_INT
+ && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1
+ && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
+ (void *) src_str, dest_align))
+ {
+ dest_mem = get_memory_rtx (dest);
+ set_mem_align (dest_mem, dest_align);
+ dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx),
+ builtin_memcpy_read_str,
+ (void *) src_str, dest_align, endp);
+ dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
+ dest_mem = convert_memory_address (ptr_mode, dest_mem);
+ return dest_mem;
}
- return expand_builtin_memcpy (arglist, target, mode, /*endp=*/1);
+ if (GET_CODE (len_rtx) == CONST_INT
+ && can_move_by_pieces (INTVAL (len_rtx),
+ MIN (dest_align, src_align)))
+ {
+ dest_mem = get_memory_rtx (dest);
+ set_mem_align (dest_mem, dest_align);
+ src_mem = get_memory_rtx (src);
+ set_mem_align (src_mem, src_align);
+ dest_mem = move_by_pieces (dest_mem, src_mem, INTVAL (len_rtx),
+ MIN (dest_align, src_align), endp);
+ dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
+ dest_mem = convert_memory_address (ptr_mode, dest_mem);
+ return dest_mem;
+ }
+
+ return 0;
}
}
if we failed the caller should emit a normal call. */
static rtx
-expand_builtin_memmove (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_memmove (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
/* If the LEN parameter is zero, return DEST. */
- if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
+ if (integer_zerop (len))
{
/* Evaluate and ignore SRC in case it has side-effects. */
expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
return expand_expr (dest, target, mode, EXPAND_NORMAL);
}
+ /* If SRC and DEST are the same (and not volatile), return DEST. */
+ if (operand_equal_p (src, dest, 0))
+ {
+ /* Evaluate and ignore LEN in case it has side-effects. */
+ expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ return expand_expr (dest, target, mode, EXPAND_NORMAL);
+ }
+
/* If either SRC is not a pointer type, don't do this
operation in-line. */
if (src_align == 0)
if we failed the caller should emit a normal call. */
static rtx
-expand_builtin_bcopy (arglist)
- tree arglist;
+expand_builtin_bcopy (tree arglist)
{
tree src, dest, size, newarglist;
convenient). */
static rtx
-expand_builtin_strcpy (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_strcpy (tree arglist, rtx target, enum machine_mode mode)
{
- tree fn, len;
+ tree fn, len, src, dst;
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ dst = TREE_VALUE (arglist);
+
+ /* If SRC and DST are equal (and not volatile), return DST. */
+ if (operand_equal_p (src, dst, 0))
+ return expand_expr (dst, target, mode, EXPAND_NORMAL);
+
fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
if (!fn)
return 0;
- len = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)));
- if (len == 0)
+ len = c_strlen (src, 1);
+ if (len == 0 || TREE_SIDE_EFFECTS (len))
return 0;
len = size_binop (PLUS_EXPR, len, ssize_int (1));
- chainon (arglist, build_tree_list (NULL_TREE, len));
+ arglist = build_tree_list (NULL_TREE, len);
+ arglist = tree_cons (NULL_TREE, src, arglist);
+ arglist = tree_cons (NULL_TREE, dst, arglist);
return expand_expr (build_function_call_expr (fn, arglist),
target, mode, EXPAND_NORMAL);
}
mode MODE if that's convenient). */
static rtx
-expand_builtin_stpcpy (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_stpcpy (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
else
{
- tree newarglist;
- tree src, len;
+ tree dst, src, len;
/* If return value is ignored, transform stpcpy into strcpy. */
if (target == const0_rtx)
{
- tree fn;
- rtx ret = expand_builtin_strcpy (arglist, target, mode);
-
- if (ret)
- return ret;
-
- fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+ tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
if (!fn)
return 0;
target, mode, EXPAND_NORMAL);
}
- /* Ensure we get an actual string who length can be evaluated at
+ /* Ensure we get an actual string whose length can be evaluated at
compile-time, not an expression containing a string. This is
because the latter will potentially produce pessimized code
when used to produce the return value. */
src = TREE_VALUE (TREE_CHAIN (arglist));
- if (! c_getstr (src) || ! (len = c_strlen (src)))
+ if (! c_getstr (src) || ! (len = c_strlen (src, 0)))
return 0;
+ dst = TREE_VALUE (arglist);
len = fold (size_binop (PLUS_EXPR, len, ssize_int (1)));
- newarglist = copy_list (arglist);
- chainon (newarglist, build_tree_list (NULL_TREE, len));
- return expand_builtin_memcpy (newarglist, target, mode, /*endp=*/2);
+ arglist = build_tree_list (NULL_TREE, len);
+ arglist = tree_cons (NULL_TREE, src, arglist);
+ arglist = tree_cons (NULL_TREE, dst, arglist);
+ return expand_builtin_mempcpy (arglist, target, mode, /*endp=*/2);
}
}
constant. */
static rtx
-builtin_strncpy_read_str (data, offset, mode)
- PTR data;
- HOST_WIDE_INT offset;
- enum machine_mode mode;
+builtin_strncpy_read_str (void *data, HOST_WIDE_INT offset,
+ enum machine_mode mode)
{
const char *str = (const char *) data;
if we failed the caller should emit a normal call. */
static rtx
-expand_builtin_strncpy (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_strncpy (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
else
{
- tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)));
+ tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 1);
tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
tree fn;
if (!p || dest_align == 0 || !host_integerp (len, 1)
|| !can_store_by_pieces (tree_low_cst (len, 1),
builtin_strncpy_read_str,
- (PTR) p, dest_align))
+ (void *) p, dest_align))
return 0;
dest_mem = get_memory_rtx (dest);
store_by_pieces (dest_mem, tree_low_cst (len, 1),
builtin_strncpy_read_str,
- (PTR) p, dest_align);
+ (void *) p, dest_align, 0);
dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (dest_mem) != ptr_mode)
- dest_mem = convert_memory_address (ptr_mode, dest_mem);
-#endif
+ dest_mem = convert_memory_address (ptr_mode, dest_mem);
return dest_mem;
}
constant. */
static rtx
-builtin_memset_read_str (data, offset, mode)
- PTR data;
- HOST_WIDE_INT offset ATTRIBUTE_UNUSED;
- enum machine_mode mode;
+builtin_memset_read_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED,
+ enum machine_mode mode)
{
const char *c = (const char *) data;
char *p = alloca (GET_MODE_SIZE (mode));
4 bytes wide, return the RTL for 0x01010101*data. */
static rtx
-builtin_memset_gen_str (data, offset, mode)
- PTR data;
- HOST_WIDE_INT offset ATTRIBUTE_UNUSED;
- enum machine_mode mode;
+builtin_memset_gen_str (void *data, HOST_WIDE_INT offset ATTRIBUTE_UNUSED,
+ enum machine_mode mode)
{
rtx target, coeff;
size_t size;
convenient). */
static rtx
-expand_builtin_memset (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist,
POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
/* If the LEN parameter is zero, return DEST. */
- if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
+ if (integer_zerop (len))
{
/* Evaluate and ignore VAL in case it has side-effects. */
expand_expr (val, const0_rtx, VOIDmode, EXPAND_NORMAL);
c = 1;
if (!can_store_by_pieces (tree_low_cst (len, 1),
builtin_memset_read_str,
- (PTR) &c, dest_align))
+ &c, dest_align))
return 0;
val = fold (build1 (CONVERT_EXPR, unsigned_char_type_node, val));
dest_mem = get_memory_rtx (dest);
store_by_pieces (dest_mem, tree_low_cst (len, 1),
builtin_memset_gen_str,
- (PTR) val_rtx, dest_align);
+ val_rtx, dest_align, 0);
dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (dest_mem) != ptr_mode)
- dest_mem = convert_memory_address (ptr_mode, dest_mem);
-#endif
+ dest_mem = convert_memory_address (ptr_mode, dest_mem);
return dest_mem;
}
if (!host_integerp (len, 1))
return 0;
if (!can_store_by_pieces (tree_low_cst (len, 1),
- builtin_memset_read_str, (PTR) &c,
+ builtin_memset_read_str, &c,
dest_align))
return 0;
dest_mem = get_memory_rtx (dest);
store_by_pieces (dest_mem, tree_low_cst (len, 1),
builtin_memset_read_str,
- (PTR) &c, dest_align);
+ &c, dest_align, 0);
dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (dest_mem) != ptr_mode)
- dest_mem = convert_memory_address (ptr_mode, dest_mem);
-#endif
+ dest_mem = convert_memory_address (ptr_mode, dest_mem);
return dest_mem;
}
if (dest_addr == 0)
{
dest_addr = force_operand (XEXP (dest_mem, 0), NULL_RTX);
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (dest_addr) != ptr_mode)
- dest_addr = convert_memory_address (ptr_mode, dest_addr);
-#endif
+ dest_addr = convert_memory_address (ptr_mode, dest_addr);
}
return dest_addr;
if we failed the caller should emit a normal call. */
static rtx
-expand_builtin_bzero (arglist)
- tree arglist;
+expand_builtin_bzero (tree arglist)
{
tree dest, size, newarglist;
TARGET, if convenient (and in mode MODE, if that's convenient). */
static rtx
-expand_builtin_memcmp (exp, arglist, target, mode)
- tree exp ATTRIBUTE_UNUSED;
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
+ enum machine_mode mode)
{
tree arg1, arg2, len;
const char *p1, *p2;
len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
/* If the len parameter is zero, return zero. */
- if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
+ if (integer_zerop (len))
{
/* Evaluate and ignore arg1 and arg2 in case they have
side-effects. */
return const0_rtx;
}
+ /* If both arguments are equal (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ {
+ /* Evaluate and ignore len in case it has side-effects. */
+ expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ return const0_rtx;
+ }
+
p1 = c_getstr (arg1);
p2 = c_getstr (arg2);
/* If len parameter is one, return an expression corresponding to
(*(const unsigned char*)arg1 - (const unsigned char*)arg2). */
- if (host_integerp (len, 1) && tree_low_cst (len, 1) == 1)
+ if (integer_onep (len))
{
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
return expand_expr (result, target, mode, EXPAND_NORMAL);
}
-#ifdef HAVE_cmpstrsi
+#if defined HAVE_cmpmemsi || defined HAVE_cmpstrsi
{
rtx arg1_rtx, arg2_rtx, arg3_rtx;
rtx result;
= get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
int arg2_align
= get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
- enum machine_mode insn_mode
- = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
+ enum machine_mode insn_mode;
+
+#ifdef HAVE_cmpmemsi
+ if (HAVE_cmpmemsi)
+ insn_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode;
+ else
+#endif
+#ifdef HAVE_cmpstrsi
+ if (HAVE_cmpstrsi)
+ insn_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
+ else
+#endif
+ return 0;
/* If we don't have POINTER_TYPE, call the function. */
if (arg1_align == 0 || arg2_align == 0)
arg1_rtx = get_memory_rtx (arg1);
arg2_rtx = get_memory_rtx (arg2);
arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
- if (!HAVE_cmpstrsi)
- insn = NULL_RTX;
+#ifdef HAVE_cmpmemsi
+ if (HAVE_cmpmemsi)
+ insn = gen_cmpmemsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
+ GEN_INT (MIN (arg1_align, arg2_align)));
else
+#endif
+#ifdef HAVE_cmpstrsi
+ if (HAVE_cmpstrsi)
insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
GEN_INT (MIN (arg1_align, arg2_align)));
+ else
+#endif
+ abort ();
if (insn)
emit_insn (insn);
XEXP (arg1_rtx, 0), Pmode,
XEXP (arg2_rtx, 0), Pmode,
convert_to_mode (TYPE_MODE (sizetype), arg3_rtx,
- TREE_UNSIGNED (sizetype)),
+ TYPE_UNSIGNED (sizetype)),
TYPE_MODE (sizetype));
/* Return the value in the proper mode for this function. */
the result in TARGET, if convenient. */
static rtx
-expand_builtin_strcmp (exp, target, mode)
- tree exp;
- rtx target;
- enum machine_mode mode;
+expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
{
tree arglist = TREE_OPERAND (exp, 1);
tree arg1, arg2;
arg1 = TREE_VALUE (arglist);
arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+ /* If both arguments are equal (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ return const0_rtx;
+
p1 = c_getstr (arg1);
p2 = c_getstr (arg2);
tree len, len1, len2;
rtx arg1_rtx, arg2_rtx, arg3_rtx;
rtx result, insn;
+ tree fndecl;
int arg1_align
= get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
enum machine_mode insn_mode
= insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
- len1 = c_strlen (arg1);
- len2 = c_strlen (arg2);
+ len1 = c_strlen (arg1, 1);
+ len2 = c_strlen (arg2, 1);
if (len1)
len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
&& REGNO (result) >= FIRST_PSEUDO_REGISTER))
result = gen_reg_rtx (insn_mode);
+ /* Stabilize the arguments in case gen_cmpstrsi fails. */
+ arg1 = save_expr (arg1);
+ arg2 = save_expr (arg2);
+
arg1_rtx = get_memory_rtx (arg1);
arg2_rtx = get_memory_rtx (arg2);
arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
GEN_INT (MIN (arg1_align, arg2_align)));
- if (!insn)
- return 0;
-
- emit_insn (insn);
+ if (insn)
+ {
+ emit_insn (insn);
+
+ /* Return the value in the proper mode for this function. */
+ mode = TYPE_MODE (TREE_TYPE (exp));
+ if (GET_MODE (result) == mode)
+ return result;
+ if (target == 0)
+ return convert_to_mode (mode, result, 0);
+ convert_move (target, result, 0);
+ return target;
+ }
- /* Return the value in the proper mode for this function. */
- mode = TYPE_MODE (TREE_TYPE (exp));
- if (GET_MODE (result) == mode)
- return result;
- if (target == 0)
- return convert_to_mode (mode, result, 0);
- convert_move (target, result, 0);
- return target;
+ /* Expand the library call ourselves using a stabilized argument
+ list to avoid re-evaluating the function's arguments twice. */
+ arglist = build_tree_list (NULL_TREE, arg2);
+ arglist = tree_cons (NULL_TREE, arg1, arglist);
+ fndecl = get_callee_fndecl (exp);
+ exp = build_function_call_expr (fndecl, arglist);
+ return expand_call (exp, target, target == const0_rtx);
}
#endif
return 0;
the result in TARGET, if convenient. */
static rtx
-expand_builtin_strncmp (exp, target, mode)
- tree exp;
- rtx target;
- enum machine_mode mode;
+expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
{
tree arglist = TREE_OPERAND (exp, 1);
tree arg1, arg2, arg3;
arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
/* If the len parameter is zero, return zero. */
- if (host_integerp (arg3, 1) && tree_low_cst (arg3, 1) == 0)
+ if (integer_zerop (arg3))
{
/* Evaluate and ignore arg1 and arg2 in case they have
side-effects. */
return const0_rtx;
}
+ /* If arg1 and arg2 are equal (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ {
+ /* Evaluate and ignore arg3 in case it has side-effects. */
+ expand_expr (arg3, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ return const0_rtx;
+ }
+
p1 = c_getstr (arg1);
p2 = c_getstr (arg2);
tree len, len1, len2;
rtx arg1_rtx, arg2_rtx, arg3_rtx;
rtx result, insn;
+ tree fndecl;
int arg1_align
= get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
enum machine_mode insn_mode
= insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
- len1 = c_strlen (arg1);
- len2 = c_strlen (arg2);
+ len1 = c_strlen (arg1, 1);
+ len2 = c_strlen (arg2, 1);
if (len1)
len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
&& REGNO (result) >= FIRST_PSEUDO_REGISTER))
result = gen_reg_rtx (insn_mode);
+ /* Stabilize the arguments in case gen_cmpstrsi fails. */
+ arg1 = save_expr (arg1);
+ arg2 = save_expr (arg2);
+ len = save_expr (len);
+
arg1_rtx = get_memory_rtx (arg1);
arg2_rtx = get_memory_rtx (arg2);
arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
GEN_INT (MIN (arg1_align, arg2_align)));
- if (!insn)
- return 0;
-
- emit_insn (insn);
+ if (insn)
+ {
+ emit_insn (insn);
+
+ /* Return the value in the proper mode for this function. */
+ mode = TYPE_MODE (TREE_TYPE (exp));
+ if (GET_MODE (result) == mode)
+ return result;
+ if (target == 0)
+ return convert_to_mode (mode, result, 0);
+ convert_move (target, result, 0);
+ return target;
+ }
- /* Return the value in the proper mode for this function. */
- mode = TYPE_MODE (TREE_TYPE (exp));
- if (GET_MODE (result) == mode)
- return result;
- if (target == 0)
- return convert_to_mode (mode, result, 0);
- convert_move (target, result, 0);
- return target;
+ /* Expand the library call ourselves using a stabilized argument
+ list to avoid re-evaluating the function's arguments twice. */
+ arglist = build_tree_list (NULL_TREE, len);
+ arglist = tree_cons (NULL_TREE, arg2, arglist);
+ arglist = tree_cons (NULL_TREE, arg1, arglist);
+ fndecl = get_callee_fndecl (exp);
+ exp = build_function_call_expr (fndecl, arglist);
+ return expand_call (exp, target, target == const0_rtx);
}
#endif
return 0;
otherwise try to get the result in TARGET, if convenient. */
static rtx
-expand_builtin_strcat (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_strcat (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
src = TREE_VALUE (TREE_CHAIN (arglist));
const char *p = c_getstr (src);
- /* If the string length is zero, return the dst parameter. */
- if (p && *p == '\0')
- return expand_expr (dst, target, mode, EXPAND_NORMAL);
+ if (p)
+ {
+ /* If the string length is zero, return the dst parameter. */
+ if (*p == '\0')
+ return expand_expr (dst, target, mode, EXPAND_NORMAL);
+ else if (!optimize_size)
+ {
+ /* Otherwise if !optimize_size, see if we can store by
+ pieces into (dst + strlen(dst)). */
+ tree newdst, arglist,
+ strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN];
+
+ /* This is the length argument. */
+ arglist = build_tree_list (NULL_TREE,
+ fold (size_binop (PLUS_EXPR,
+ c_strlen (src, 0),
+ ssize_int (1))));
+ /* Prepend src argument. */
+ arglist = tree_cons (NULL_TREE, src, arglist);
+
+ /* We're going to use dst more than once. */
+ dst = save_expr (dst);
+
+ /* Create strlen (dst). */
+ newdst =
+ fold (build_function_call_expr (strlen_fn,
+ build_tree_list (NULL_TREE,
+ dst)));
+ /* Create (dst + strlen (dst)). */
+ newdst = fold (build (PLUS_EXPR, TREE_TYPE (dst), dst, newdst));
+
+ /* Prepend the new dst argument. */
+ arglist = tree_cons (NULL_TREE, newdst, arglist);
+
+ /* We don't want to get turned into a memcpy if the
+ target is const0_rtx, i.e. when the return value
+ isn't used. That would produce pessimized code so
+ pass in a target of zero, it should never actually be
+ used. If this was successful return the original
+ dst, not the result of mempcpy. */
+ if (expand_builtin_mempcpy (arglist, /*target=*/0, mode, /*endp=*/0))
+ return expand_expr (dst, target, mode, EXPAND_NORMAL);
+ else
+ return 0;
+ }
+ }
return 0;
}
otherwise try to get the result in TARGET, if convenient. */
static rtx
-expand_builtin_strncat (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_strncat (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
otherwise try to get the result in TARGET, if convenient. */
static rtx
-expand_builtin_strspn (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_strspn (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
otherwise try to get the result in TARGET, if convenient. */
static rtx
-expand_builtin_strcspn (arglist, target, mode)
- tree arglist;
- rtx target;
- enum machine_mode mode;
+expand_builtin_strcspn (tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
if that's convenient. */
rtx
-expand_builtin_saveregs ()
+expand_builtin_saveregs (void)
{
rtx val, seq;
start_sequence ();
-#ifdef EXPAND_BUILTIN_SAVEREGS
/* Do whatever the machine needs done in this case. */
- val = EXPAND_BUILTIN_SAVEREGS ();
-#else
- /* ??? We used to try and build up a call to the out of line function,
- guessing about what registers needed saving etc. This became much
- harder with __builtin_va_start, since we don't have a tree for a
- call to __builtin_saveregs to fall back on. There was exactly one
- port (i860) that used this code, and I'm unconvinced it could actually
- handle the general case. So we no longer try to handle anything
- weird and make the backend absorb the evil. */
-
- error ("__builtin_saveregs not supported by this target");
- val = const0_rtx;
-#endif
+ val = targetm.calls.expand_builtin_saveregs ();
seq = get_insns ();
end_sequence ();
is controlled by the definition of CUMULATIVE_ARGS. */
static rtx
-expand_builtin_args_info (arglist)
- tree arglist;
+expand_builtin_args_info (tree arglist)
{
int nwords = sizeof (CUMULATIVE_ARGS) / sizeof (int);
int *word_ptr = (int *) ¤t_function_args_info;
/* Expand ARGLIST, from a call to __builtin_next_arg. */
static rtx
-expand_builtin_next_arg (arglist)
- tree arglist;
+expand_builtin_next_arg (tree arglist)
{
tree fntype = TREE_TYPE (current_function_decl);
from multiple evaluations. */
static tree
-stabilize_va_list (valist, needs_lvalue)
- tree valist;
- int needs_lvalue;
+stabilize_va_list (tree valist, int needs_lvalue)
{
if (TREE_CODE (va_list_type_node) == ARRAY_TYPE)
{
return valist;
}
+/* The "standard" definition of va_list is void*. */
+
+tree
+std_build_builtin_va_list (void)
+{
+ return ptr_type_node;
+}
+
/* The "standard" implementation of va_start: just assign `nextarg' to
the variable. */
void
-std_expand_builtin_va_start (valist, nextarg)
- tree valist;
- rtx nextarg;
+std_expand_builtin_va_start (tree valist, rtx nextarg)
{
tree t;
/* Expand ARGLIST, from a call to __builtin_va_start. */
static rtx
-expand_builtin_va_start (arglist)
- tree arglist;
+expand_builtin_va_start (tree arglist)
{
rtx nextarg;
tree chain, valist;
current (padded) address and increment by the (padded) size. */
rtx
-std_expand_builtin_va_arg (valist, type)
- tree valist, type;
+std_expand_builtin_va_arg (tree valist, tree type)
{
tree addr_tree, t, type_size = NULL;
tree align, alignm1;
tree rounded_size;
rtx addr;
+ HOST_WIDE_INT boundary;
/* Compute the rounded size of the type. */
align = size_int (PARM_BOUNDARY / BITS_PER_UNIT);
alignm1 = size_int (PARM_BOUNDARY / BITS_PER_UNIT - 1);
+ boundary = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type);
+
+ /* va_list pointer is aligned to PARM_BOUNDARY. If argument actually
+ requires greater alignment, we must perform dynamic alignment. */
+
+ if (boundary > PARM_BOUNDARY)
+ {
+ if (!PAD_VARARGS_DOWN)
+ {
+ t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
+ build (PLUS_EXPR, TREE_TYPE (valist), valist,
+ build_int_2 (boundary / BITS_PER_UNIT - 1, 0)));
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
+ t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
+ build (BIT_AND_EXPR, TREE_TYPE (valist), valist,
+ build_int_2 (~(boundary / BITS_PER_UNIT - 1), -1)));
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
if (type == error_mark_node
|| (type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type))) == NULL
|| TREE_OVERFLOW (type_size))
a very special sort of operator. */
rtx
-expand_builtin_va_arg (valist, type)
- tree valist, type;
+expand_builtin_va_arg (tree valist, tree type)
{
rtx addr, result;
tree promoted_type, want_va_type, have_va_type;
/* Generate a diagnostic for requesting data of a type that cannot
be passed through `...' due to type promotion at the call site. */
- else if ((promoted_type = (*lang_hooks.types.type_promotes_to) (type))
+ else if ((promoted_type = lang_hooks.types.type_promotes_to (type))
!= type)
{
const char *name = "<anonymous type>", *pname = 0;
/* We can, however, treat "undefined" any way we please.
Call abort to encourage the user to fix the program. */
+ inform ("if this code is reached, the program will abort");
expand_builtin_trap ();
/* This is dead code, but go ahead and finish so that the
#endif
}
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (addr) != Pmode)
- addr = convert_memory_address (Pmode, addr);
-#endif
+ addr = convert_memory_address (Pmode, addr);
result = gen_rtx_MEM (TYPE_MODE (type), addr);
set_mem_alias_set (result, get_varargs_alias_set ());
/* Expand ARGLIST, from a call to __builtin_va_end. */
static rtx
-expand_builtin_va_end (arglist)
- tree arglist;
+expand_builtin_va_end (tree arglist)
{
tree valist = TREE_VALUE (arglist);
-#ifdef EXPAND_BUILTIN_VA_END
- valist = stabilize_va_list (valist, 0);
- EXPAND_BUILTIN_VA_END (arglist);
-#else
/* Evaluate for side effects, if needed. I hate macros that don't
do that. */
if (TREE_SIDE_EFFECTS (valist))
expand_expr (valist, const0_rtx, VOIDmode, EXPAND_NORMAL);
-#endif
return const0_rtx;
}
nastiness of array-type va_list types. */
static rtx
-expand_builtin_va_copy (arglist)
- tree arglist;
+expand_builtin_va_copy (tree arglist)
{
tree dst, src, t;
size = expand_expr (TYPE_SIZE_UNIT (va_list_type_node), NULL_RTX,
VOIDmode, EXPAND_NORMAL);
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (dstb) != Pmode)
- dstb = convert_memory_address (Pmode, dstb);
-
- if (GET_MODE (srcb) != Pmode)
- srcb = convert_memory_address (Pmode, srcb);
-#endif
+ dstb = convert_memory_address (Pmode, dstb);
+ srcb = convert_memory_address (Pmode, srcb);
/* "Dereference" to BLKmode memories. */
dstb = gen_rtx_MEM (BLKmode, dstb);
__builtin_return_address. */
static rtx
-expand_builtin_frame_address (fndecl, arglist)
- tree fndecl, arglist;
+expand_builtin_frame_address (tree fndecl, tree arglist)
{
/* The argument must be a nonnegative integer constant.
It counts the number of frames to scan up the stack.
the result in TARGET, if convenient. */
static rtx
-expand_builtin_alloca (arglist, target)
- tree arglist;
- rtx target;
+expand_builtin_alloca (tree arglist, rtx target)
{
rtx op0;
rtx result;
/* Allocate the desired space. */
result = allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT);
-
-#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE (result) != ptr_mode)
- result = convert_memory_address (ptr_mode, result);
-#endif
+ result = convert_memory_address (ptr_mode, result);
return result;
}
SUBTARGET may be used as the target for computing one of EXP's operands. */
static rtx
-expand_builtin_unop (target_mode, arglist, target, subtarget, op_optab)
- enum machine_mode target_mode;
- tree arglist;
- rtx target, subtarget;
- optab op_optab;
+expand_builtin_unop (enum machine_mode target_mode, tree arglist, rtx target,
+ rtx subtarget, optab op_optab)
{
rtx op0;
if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
long, we attempt to transform this call into __builtin_fputc(). */
static rtx
-expand_builtin_fputs (arglist, ignore, unlocked)
- tree arglist;
- int ignore;
- int unlocked;
+expand_builtin_fputs (tree arglist, rtx target, bool unlocked)
{
tree len, fn;
tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
/* If the return value is used, or the replacement _DECL isn't
initialized, don't do the transformation. */
- if (!ignore || !fn_fputc || !fn_fwrite)
+ if (target != const0_rtx || !fn_fputc || !fn_fwrite)
return 0;
/* Verify the arguments in the original call. */
/* Get the length of the string passed to fputs. If the length
can't be determined, punt. */
- if (!(len = c_strlen (TREE_VALUE (arglist)))
+ if (!(len = c_strlen (TREE_VALUE (arglist), 1))
|| TREE_CODE (len) != INTEGER_CST)
return 0;
break;
}
}
- /* FALLTHROUGH */
+ /* Fall through. */
case 1: /* length is greater than 1, call fwrite. */
{
tree string_arg;
- /* If optimizing for size keep fputs. */
+ /* If optimizing for size keep fputs. */
if (optimize_size)
return 0;
string_arg = TREE_VALUE (arglist);
}
return expand_expr (build_function_call_expr (fn, arglist),
- (ignore ? const0_rtx : NULL_RTX),
- VOIDmode, EXPAND_NORMAL);
+ const0_rtx, VOIDmode, EXPAND_NORMAL);
}
/* Expand a call to __builtin_expect. We return our argument and emit a
a non-jump context. */
static rtx
-expand_builtin_expect (arglist, target)
- tree arglist;
- rtx target;
+expand_builtin_expect (tree arglist, rtx target)
{
tree exp, c;
rtx note, rtx_c;
rtx_c = expand_expr (c, NULL_RTX, GET_MODE (target), EXPAND_NORMAL);
- note = emit_note (NULL, NOTE_INSN_EXPECTED_VALUE);
+ note = emit_note (NOTE_INSN_EXPECTED_VALUE);
NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, target, rtx_c);
}
based on the test being 0/1. */
rtx
-expand_builtin_expect_jump (exp, if_false_label, if_true_label)
- tree exp;
- rtx if_false_label;
- rtx if_true_label;
+expand_builtin_expect_jump (tree exp, rtx if_false_label, rtx if_true_label)
{
tree arglist = TREE_OPERAND (exp, 1);
tree arg0 = TREE_VALUE (arglist);
if (TREE_CODE (TREE_TYPE (arg1)) == INTEGER_TYPE
&& (integer_zerop (arg1) || integer_onep (arg1)))
{
- int num_jumps = 0;
- rtx insn;
-
- /* If we fail to locate an appropriate conditional jump, we'll
- fall back to normal evaluation. Ensure that the expression
- can be re-evaluated. */
- switch (unsafe_for_reeval (arg0))
- {
- case 0: /* Safe. */
- break;
-
- case 1: /* Mildly unsafe. */
- arg0 = unsave_expr (arg0);
- break;
-
- case 2: /* Wildly unsafe. */
- return NULL_RTX;
- }
+ rtx insn, drop_through_label, temp;
/* Expand the jump insns. */
start_sequence ();
do_jump (arg0, if_false_label, if_true_label);
ret = get_insns ();
+
+ drop_through_label = get_last_insn ();
+ if (drop_through_label && GET_CODE (drop_through_label) == NOTE)
+ drop_through_label = prev_nonnote_insn (drop_through_label);
+ if (drop_through_label && GET_CODE (drop_through_label) != CODE_LABEL)
+ drop_through_label = NULL_RTX;
end_sequence ();
- /* Now that the __builtin_expect has been validated, go through and add
- the expect's to each of the conditional jumps. If we run into an
- error, just give up and generate the 'safe' code of doing a SCC
- operation and then doing a branch on that. */
+ if (! if_true_label)
+ if_true_label = drop_through_label;
+ if (! if_false_label)
+ if_false_label = drop_through_label;
+
+ /* Go through and add the expect's to each of the conditional jumps. */
insn = ret;
while (insn != NULL_RTX)
{
rtx next = NEXT_INSN (insn);
- rtx pattern;
- if (GET_CODE (insn) == JUMP_INSN && any_condjump_p (insn)
- && (pattern = pc_set (insn)) != NULL_RTX)
+ if (GET_CODE (insn) == JUMP_INSN && any_condjump_p (insn))
{
- rtx ifelse = SET_SRC (pattern);
- rtx label;
- int taken;
-
- if (GET_CODE (ifelse) != IF_THEN_ELSE)
- goto do_next_insn;
-
- if (GET_CODE (XEXP (ifelse, 1)) == LABEL_REF)
- {
- taken = 1;
- label = XEXP (XEXP (ifelse, 1), 0);
- }
- /* An inverted jump reverses the probabilities. */
- else if (GET_CODE (XEXP (ifelse, 2)) == LABEL_REF)
+ rtx ifelse = SET_SRC (pc_set (insn));
+ rtx then_dest = XEXP (ifelse, 1);
+ rtx else_dest = XEXP (ifelse, 2);
+ int taken = -1;
+
+ /* First check if we recognize any of the labels. */
+ if (GET_CODE (then_dest) == LABEL_REF
+ && XEXP (then_dest, 0) == if_true_label)
+ taken = 1;
+ else if (GET_CODE (then_dest) == LABEL_REF
+ && XEXP (then_dest, 0) == if_false_label)
+ taken = 0;
+ else if (GET_CODE (else_dest) == LABEL_REF
+ && XEXP (else_dest, 0) == if_false_label)
+ taken = 1;
+ else if (GET_CODE (else_dest) == LABEL_REF
+ && XEXP (else_dest, 0) == if_true_label)
+ taken = 0;
+ /* Otherwise check where we drop through. */
+ else if (else_dest == pc_rtx)
{
- taken = 0;
- label = XEXP (XEXP (ifelse, 2), 0);
+ if (next && GET_CODE (next) == NOTE)
+ next = next_nonnote_insn (next);
+
+ if (next && GET_CODE (next) == JUMP_INSN
+ && any_uncondjump_p (next))
+ temp = XEXP (SET_SRC (pc_set (next)), 0);
+ else
+ temp = next;
+
+ /* TEMP is either a CODE_LABEL, NULL_RTX or something
+ else that can't possibly match either target label. */
+ if (temp == if_false_label)
+ taken = 1;
+ else if (temp == if_true_label)
+ taken = 0;
}
- /* We shouldn't have to worry about conditional returns during
- the expansion stage, but handle it gracefully anyway. */
- else if (GET_CODE (XEXP (ifelse, 1)) == RETURN)
+ else if (then_dest == pc_rtx)
{
- taken = 1;
- label = NULL_RTX;
+ if (next && GET_CODE (next) == NOTE)
+ next = next_nonnote_insn (next);
+
+ if (next && GET_CODE (next) == JUMP_INSN
+ && any_uncondjump_p (next))
+ temp = XEXP (SET_SRC (pc_set (next)), 0);
+ else
+ temp = next;
+
+ if (temp == if_false_label)
+ taken = 0;
+ else if (temp == if_true_label)
+ taken = 1;
}
- /* An inverted return reverses the probabilities. */
- else if (GET_CODE (XEXP (ifelse, 2)) == RETURN)
+
+ if (taken != -1)
{
- taken = 0;
- label = NULL_RTX;
+ /* If the test is expected to fail, reverse the
+ probabilities. */
+ if (integer_zerop (arg1))
+ taken = 1 - taken;
+ predict_insn_def (insn, PRED_BUILTIN_EXPECT, taken);
}
- else
- goto do_next_insn;
-
- /* If the test is expected to fail, reverse the
- probabilities. */
- if (integer_zerop (arg1))
- taken = 1 - taken;
-
- /* If we are jumping to the false label, reverse the
- probabilities. */
- if (label == NULL_RTX)
- ; /* conditional return */
- else if (label == if_false_label)
- taken = 1 - taken;
- else if (label != if_true_label)
- goto do_next_insn;
-
- num_jumps++;
- predict_insn_def (insn, PRED_BUILTIN_EXPECT, taken);
}
- do_next_insn:
insn = next;
}
-
- /* If no jumps were modified, fail and do __builtin_expect the normal
- way. */
- if (num_jumps == 0)
- ret = NULL_RTX;
}
return ret;
}
void
-expand_builtin_trap ()
+expand_builtin_trap (void)
{
#ifdef HAVE_trap
if (HAVE_trap)
emit_library_call (abort_libfunc, LCT_NORETURN, VOIDmode, 0);
emit_barrier ();
}
-\f
-/* 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).
- SUBTARGET may be used as the target for computing one of EXP's operands.
- IGNORE is nonzero if the value is to be ignored. */
-rtx
-expand_builtin (exp, target, subtarget, mode, ignore)
- tree exp;
- rtx target;
- rtx subtarget;
- enum machine_mode mode;
- int ignore;
-{
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
- tree arglist = TREE_OPERAND (exp, 1);
- enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
- enum machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp));
+/* Expand a call to fabs, fabsf or fabsl with arguments ARGLIST.
+ Return 0 if a normal call should be emitted rather than expanding
+ the function inline. If convenient, the result should be placed
+ in TARGET. SUBTARGET may be used as the target for computing
+ the operand. */
- /* Perform postincrements before expanding builtin functions. Â */
- emit_queue ();
+static rtx
+expand_builtin_fabs (tree arglist, rtx target, rtx subtarget)
+{
+ enum machine_mode mode;
+ tree arg;
+ rtx op0;
- if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
- return (*targetm.expand_builtin) (exp, target, subtarget, mode, ignore);
+ if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
- /* When not optimizing, generate calls to library functions for a certain
- set of builtins. */
- if (!optimize && !CALLED_AS_BUILT_IN (fndecl))
- switch (fcode)
- {
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
- case BUILT_IN_SIN:
- case BUILT_IN_SINF:
- case BUILT_IN_SINL:
- case BUILT_IN_COS:
- case BUILT_IN_COSF:
- case BUILT_IN_COSL:
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
- case BUILT_IN_TAN:
- case BUILT_IN_TANF:
- case BUILT_IN_TANL:
- case BUILT_IN_ATAN:
- case BUILT_IN_ATANF:
- case BUILT_IN_ATANL:
- case BUILT_IN_POW:
- case BUILT_IN_POWF:
- case BUILT_IN_POWL:
- case BUILT_IN_ATAN2:
- case BUILT_IN_ATAN2F:
- case BUILT_IN_ATAN2L:
- case BUILT_IN_MEMSET:
- case BUILT_IN_MEMCPY:
- case BUILT_IN_MEMCMP:
- case BUILT_IN_MEMPCPY:
- case BUILT_IN_MEMMOVE:
- case BUILT_IN_BCMP:
- case BUILT_IN_BZERO:
- case BUILT_IN_BCOPY:
- case BUILT_IN_INDEX:
- case BUILT_IN_RINDEX:
- case BUILT_IN_STPCPY:
- case BUILT_IN_STRCHR:
- case BUILT_IN_STRRCHR:
- case BUILT_IN_STRLEN:
- case BUILT_IN_STRCPY:
- case BUILT_IN_STRNCPY:
- case BUILT_IN_STRNCMP:
- case BUILT_IN_STRSTR:
- case BUILT_IN_STRPBRK:
- case BUILT_IN_STRCAT:
- case BUILT_IN_STRNCAT:
- case BUILT_IN_STRSPN:
- case BUILT_IN_STRCSPN:
- case BUILT_IN_STRCMP:
- case BUILT_IN_FFS:
- case BUILT_IN_PUTCHAR:
- case BUILT_IN_PUTS:
- case BUILT_IN_PRINTF:
- case BUILT_IN_FPUTC:
- case BUILT_IN_FPUTS:
- case BUILT_IN_FWRITE:
- case BUILT_IN_PUTCHAR_UNLOCKED:
- case BUILT_IN_PUTS_UNLOCKED:
- case BUILT_IN_PRINTF_UNLOCKED:
- case BUILT_IN_FPUTC_UNLOCKED:
- case BUILT_IN_FPUTS_UNLOCKED:
- case BUILT_IN_FWRITE_UNLOCKED:
- case BUILT_IN_FLOOR:
- case BUILT_IN_FLOORF:
- case BUILT_IN_FLOORL:
- case BUILT_IN_CEIL:
- case BUILT_IN_CEILF:
- case BUILT_IN_CEILL:
- case BUILT_IN_TRUNC:
- case BUILT_IN_TRUNCF:
- case BUILT_IN_TRUNCL:
- case BUILT_IN_ROUND:
- case BUILT_IN_ROUNDF:
- case BUILT_IN_ROUNDL:
- case BUILT_IN_NEARBYINT:
- case BUILT_IN_NEARBYINTF:
- case BUILT_IN_NEARBYINTL:
- return expand_call (exp, target, ignore);
+ arg = TREE_VALUE (arglist);
+ mode = TYPE_MODE (TREE_TYPE (arg));
+ op0 = expand_expr (arg, subtarget, VOIDmode, 0);
+ return expand_abs (mode, op0, target, 0, safe_from_p (target, arg, 1));
+}
- default:
- break;
- }
+/* Expand a call to cabs, cabsf or cabsl with arguments ARGLIST.
+ Return 0 if a normal call should be emitted rather than expanding
+ the function inline. If convenient, the result should be placed
+ in target. */
- /* The built-in function expanders test for target == const0_rtx
- to determine whether the function's result will be ignored. */
- if (ignore)
- target = const0_rtx;
+static rtx
+expand_builtin_cabs (tree arglist, rtx target)
+{
+ enum machine_mode mode;
+ tree arg;
+ rtx op0;
- /* If the result of a pure or const built-in function is ignored, and
- none of its arguments are volatile, we can avoid expanding the
- built-in call and just evaluate the arguments for side-effects. */
- if (target == const0_rtx
- && (DECL_IS_PURE (fndecl) || TREE_READONLY (fndecl)))
- {
- bool volatilep = false;
- tree arg;
+ if (arglist == 0 || TREE_CHAIN (arglist))
+ return 0;
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (arg)) != COMPLEX_TYPE
+ || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != REAL_TYPE)
+ return 0;
- for (arg = arglist; arg; arg = TREE_CHAIN (arg))
- if (TREE_THIS_VOLATILE (TREE_VALUE (arg)))
- {
- volatilep = true;
- break;
- }
+ mode = TYPE_MODE (TREE_TYPE (arg));
+ op0 = expand_expr (arg, NULL_RTX, VOIDmode, 0);
+ return expand_complex_abs (mode, op0, target, 0);
+}
- if (! volatilep)
- {
- for (arg = arglist; arg; arg = TREE_CHAIN (arg))
- expand_expr (TREE_VALUE (arg), const0_rtx,
- VOIDmode, EXPAND_NORMAL);
- return const0_rtx;
- }
- }
+/* Create a new constant string literal and return a char* pointer to it.
+ The STRING_CST value is the LEN characters at STR. */
+static tree
+build_string_literal (int len, const char *str)
+{
+ tree t, elem, index, type;
+
+ t = build_string (len, str);
+ elem = build_type_variant (char_type_node, 1, 0);
+ index = build_index_type (build_int_2 (len - 1, 0));
+ type = build_array_type (elem, index);
+ TREE_TYPE (t) = type;
+ TREE_CONSTANT (t) = 1;
+ TREE_READONLY (t) = 1;
+ TREE_STATIC (t) = 1;
+
+ type = build_pointer_type (type);
+ t = build1 (ADDR_EXPR, type, t);
+
+ type = build_pointer_type (elem);
+ t = build1 (NOP_EXPR, type, t);
+ return t;
+}
- switch (fcode)
- {
- case BUILT_IN_ABS:
- case BUILT_IN_LABS:
- case BUILT_IN_LLABS:
- case BUILT_IN_IMAXABS:
- case BUILT_IN_FABS:
- case BUILT_IN_FABSF:
- case BUILT_IN_FABSL:
- /* build_function_call changes these into ABS_EXPR. */
- abort ();
+/* Expand a call to printf or printf_unlocked with argument list ARGLIST.
+ Return 0 if a normal call should be emitted rather than transforming
+ the function inline. If convenient, the result should be placed in
+ TARGET with mode MODE. UNLOCKED indicates this is a printf_unlocked
+ call. */
+static rtx
+expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode,
+ bool unlocked)
+{
+ tree fn_putchar = unlocked
+ ? implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_PUTCHAR];
+ tree fn_puts = unlocked ? implicit_built_in_decls[BUILT_IN_PUTS_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_PUTS];
+ const char *fmt_str;
+ tree fn, fmt, arg;
+
+ /* If the return value is used, don't do the transformation. */
+ if (target != const0_rtx)
+ return 0;
- case BUILT_IN_CONJ:
- case BUILT_IN_CONJF:
- case BUILT_IN_CONJL:
- case BUILT_IN_CREAL:
- case BUILT_IN_CREALF:
- case BUILT_IN_CREALL:
- case BUILT_IN_CIMAG:
- case BUILT_IN_CIMAGF:
- case BUILT_IN_CIMAGL:
- /* expand_tree_builtin changes these into CONJ_EXPR, REALPART_EXPR
- and IMAGPART_EXPR. */
- abort ();
+ /* Verify the required arguments in the original call. */
+ if (! arglist)
+ return 0;
+ fmt = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
- case BUILT_IN_SIN:
- case BUILT_IN_SINF:
- case BUILT_IN_SINL:
- case BUILT_IN_COS:
- case BUILT_IN_COSF:
- case BUILT_IN_COSL:
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
- /* Treat these like sqrt only if unsafe math optimizations are allowed,
- because of possible accuracy problems. */
- if (! flag_unsafe_math_optimizations)
- break;
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
- case BUILT_IN_FLOOR:
- case BUILT_IN_FLOORF:
- case BUILT_IN_FLOORL:
- case BUILT_IN_CEIL:
- case BUILT_IN_CEILF:
- case BUILT_IN_CEILL:
- case BUILT_IN_TRUNC:
- case BUILT_IN_TRUNCF:
- case BUILT_IN_TRUNCL:
- case BUILT_IN_ROUND:
- case BUILT_IN_ROUNDF:
- case BUILT_IN_ROUNDL:
- case BUILT_IN_NEARBYINT:
- case BUILT_IN_NEARBYINTF:
- case BUILT_IN_NEARBYINTL:
- target = expand_builtin_mathfn (exp, target, subtarget);
- if (target)
- return target;
- break;
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return 0;
- case BUILT_IN_POW:
- case BUILT_IN_POWF:
- case BUILT_IN_POWL:
- case BUILT_IN_ATAN2:
- case BUILT_IN_ATAN2F:
- case BUILT_IN_ATAN2L:
- if (! flag_unsafe_math_optimizations)
- break;
- target = expand_builtin_mathfn_2 (exp, target, subtarget);
- if (target)
- return target;
- break;
+ /* If the format specifier was "%s\n", call __builtin_puts(arg). */
+ if (strcmp (fmt_str, "%s\n") == 0)
+ {
+ if (! arglist
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist))
+ return 0;
+ fn = fn_puts;
+ }
+ /* If the format specifier was "%c", call __builtin_putchar(arg). */
+ else if (strcmp (fmt_str, "%c") == 0)
+ {
+ if (! arglist
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
+ || TREE_CHAIN (arglist))
+ return 0;
+ fn = fn_putchar;
+ }
+ else
+ {
+ /* We can't handle anything else with % args or %% ... yet. */
+ if (strchr (fmt_str, '%'))
+ return 0;
- case BUILT_IN_APPLY_ARGS:
- return expand_builtin_apply_args ();
+ if (arglist)
+ return 0;
- /* __builtin_apply (FUNCTION, ARGUMENTS, ARGSIZE) invokes
- FUNCTION with a copy of the parameters described by
- ARGUMENTS, and ARGSIZE. It returns a block of memory
- allocated on the stack into which is stored all the registers
- that might possibly be used for returning the result of a
- function. ARGUMENTS is the value returned by
- __builtin_apply_args. ARGSIZE is the number of bytes of
- arguments that must be copied. ??? How should this value be
- computed? We'll also need a safe worst case value for varargs
- functions. */
- case BUILT_IN_APPLY:
- if (!validate_arglist (arglist, POINTER_TYPE,
- POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
- && !validate_arglist (arglist, REFERENCE_TYPE,
- POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ /* If the format specifier was "", printf does nothing. */
+ if (fmt_str[0] == '\0')
return const0_rtx;
+ /* If the format specifier has length of 1, call putchar. */
+ if (fmt_str[1] == '\0')
+ {
+ /* Given printf("c"), (where c is any one character,)
+ convert "c"[0] to an int and pass that to the replacement
+ function. */
+ arg = build_int_2 (fmt_str[0], 0);
+ arglist = build_tree_list (NULL_TREE, arg);
+ fn = fn_putchar;
+ }
else
{
- int i;
- tree t;
- rtx ops[3];
-
- for (t = arglist, i = 0; t; t = TREE_CHAIN (t), i++)
- ops[i] = expand_expr (TREE_VALUE (t), NULL_RTX, VOIDmode, 0);
+ /* If the format specifier was "string\n", call puts("string"). */
+ size_t len = strlen (fmt_str);
+ if (fmt_str[len - 1] == '\n')
+ {
+ /* Create a NUL-terminated string that's one char shorter
+ than the original, stripping off the trailing '\n'. */
+ char *newstr = alloca (len);
+ memcpy (newstr, fmt_str, len - 1);
+ newstr[len - 1] = 0;
- return expand_builtin_apply (ops[0], ops[1], ops[2]);
+ arg = build_string_literal (len, newstr);
+ arglist = build_tree_list (NULL_TREE, arg);
+ fn = fn_puts;
+ }
+ else
+ /* We'd like to arrange to call fputs(string,stdout) here,
+ but we need stdout and don't have a way to get it yet. */
+ return 0;
}
+ }
- /* __builtin_return (RESULT) causes the function to return the
- value described by RESULT. RESULT is address of the block of
- memory returned by __builtin_apply. */
- case BUILT_IN_RETURN:
- if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
- expand_builtin_return (expand_expr (TREE_VALUE (arglist),
- NULL_RTX, VOIDmode, 0));
- return const0_rtx;
+ if (!fn)
+ return 0;
+ return expand_expr (build_function_call_expr (fn, arglist),
+ target, mode, EXPAND_NORMAL);
+}
- case BUILT_IN_SAVEREGS:
- return expand_builtin_saveregs ();
+/* Expand a call to fprintf or fprintf_unlocked with argument list ARGLIST.
+ Return 0 if a normal call should be emitted rather than transforming
+ the function inline. If convenient, the result should be placed in
+ TARGET with mode MODE. UNLOCKED indicates this is a fprintf_unlocked
+ call. */
+static rtx
+expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode,
+ bool unlocked)
+{
+ tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_FPUTC];
+ tree fn_fputs = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTS_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_FPUTS];
+ const char *fmt_str;
+ tree fn, fmt, fp, arg;
+
+ /* If the return value is used, don't do the transformation. */
+ if (target != const0_rtx)
+ return 0;
- case BUILT_IN_ARGS_INFO:
- return expand_builtin_args_info (arglist);
+ /* Verify the required arguments in the original call. */
+ if (! arglist)
+ return 0;
+ fp = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (fp)) != POINTER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return 0;
+ fmt = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
- /* Return the address of the first anonymous stack arg. */
- case BUILT_IN_NEXT_ARG:
- return expand_builtin_next_arg (arglist);
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return 0;
- case BUILT_IN_CLASSIFY_TYPE:
- return expand_builtin_classify_type (arglist);
+ /* If the format specifier was "%s", call __builtin_fputs(arg,fp). */
+ if (strcmp (fmt_str, "%s") == 0)
+ {
+ if (! arglist
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist))
+ return 0;
+ arg = TREE_VALUE (arglist);
+ arglist = build_tree_list (NULL_TREE, fp);
+ arglist = tree_cons (NULL_TREE, arg, arglist);
+ fn = fn_fputs;
+ }
+ /* If the format specifier was "%c", call __builtin_fputc(arg,fp). */
+ else if (strcmp (fmt_str, "%c") == 0)
+ {
+ if (! arglist
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
+ || TREE_CHAIN (arglist))
+ return 0;
+ arg = TREE_VALUE (arglist);
+ arglist = build_tree_list (NULL_TREE, fp);
+ arglist = tree_cons (NULL_TREE, arg, arglist);
+ fn = fn_fputc;
+ }
+ else
+ {
+ /* We can't handle anything else with % args or %% ... yet. */
+ if (strchr (fmt_str, '%'))
+ return 0;
- case BUILT_IN_CONSTANT_P:
- return expand_builtin_constant_p (arglist, target_mode);
+ if (arglist)
+ return 0;
- case BUILT_IN_FRAME_ADDRESS:
- case BUILT_IN_RETURN_ADDRESS:
- return expand_builtin_frame_address (fndecl, arglist);
+ /* If the format specifier was "", fprintf does nothing. */
+ if (fmt_str[0] == '\0')
+ {
+ /* Evaluate and ignore FILE* argument for side-effects. */
+ expand_expr (fp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ return const0_rtx;
+ }
- /* Returns the address of the area where the structure is returned.
- 0 otherwise. */
- case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
- if (arglist != 0
- || ! AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))
- || GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) != MEM)
+ /* When "string" doesn't contain %, replace all cases of
+ fprintf(stream,string) with fputs(string,stream). The fputs
+ builtin will take care of special cases like length == 1. */
+ arglist = build_tree_list (NULL_TREE, fp);
+ arglist = tree_cons (NULL_TREE, fmt, arglist);
+ fn = fn_fputs;
+ }
+
+ if (!fn)
+ return 0;
+ return expand_expr (build_function_call_expr (fn, arglist),
+ target, mode, EXPAND_NORMAL);
+}
+
+/* Expand a call to sprintf with argument list ARGLIST. Return 0 if
+ a normal call should be emitted rather than expanding the function
+ inline. If convenient, the result should be placed in TARGET with
+ mode MODE. */
+
+static rtx
+expand_builtin_sprintf (tree arglist, rtx target, enum machine_mode mode)
+{
+ tree orig_arglist, dest, fmt;
+ const char *fmt_str;
+
+ orig_arglist = arglist;
+
+ /* Verify the required arguments in the original call. */
+ if (! arglist)
+ return 0;
+ dest = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (dest)) != POINTER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return 0;
+ fmt = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return 0;
+
+ /* If the format doesn't contain % args or %%, use strcpy. */
+ if (strchr (fmt_str, '%') == 0)
+ {
+ tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+ tree exp;
+
+ if (arglist || ! fn)
+ return 0;
+ expand_expr (build_function_call_expr (fn, orig_arglist),
+ const0_rtx, VOIDmode, EXPAND_NORMAL);
+ if (target == const0_rtx)
return const0_rtx;
+ exp = build_int_2 (strlen (fmt_str), 0);
+ exp = fold (build1 (NOP_EXPR, integer_type_node, exp));
+ return expand_expr (exp, target, mode, EXPAND_NORMAL);
+ }
+ /* If the format is "%s", use strcpy if the result isn't used. */
+ else if (strcmp (fmt_str, "%s") == 0)
+ {
+ tree fn, arg, len;
+ fn = implicit_built_in_decls[BUILT_IN_STRCPY];
+
+ if (! fn)
+ return 0;
+
+ if (! arglist || TREE_CHAIN (arglist))
+ return 0;
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
+ return 0;
+
+ if (target != const0_rtx)
+ {
+ len = c_strlen (arg, 1);
+ if (! len || TREE_CODE (len) != INTEGER_CST)
+ return 0;
+ }
else
- return XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0);
+ len = NULL_TREE;
- case BUILT_IN_ALLOCA:
- target = expand_builtin_alloca (arglist, target);
- if (target)
- return target;
- break;
+ arglist = build_tree_list (NULL_TREE, arg);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+ expand_expr (build_function_call_expr (fn, arglist),
+ const0_rtx, VOIDmode, EXPAND_NORMAL);
- case BUILT_IN_FFS:
- case BUILT_IN_FFSL:
- case BUILT_IN_FFSLL:
- target = expand_builtin_unop (target_mode, arglist, target,
- subtarget, ffs_optab);
- if (target)
- return target;
- break;
+ if (target == const0_rtx)
+ return const0_rtx;
+ return expand_expr (len, target, mode, EXPAND_NORMAL);
+ }
- case BUILT_IN_CLZ:
- case BUILT_IN_CLZL:
- case BUILT_IN_CLZLL:
- target = expand_builtin_unop (target_mode, arglist, target,
- subtarget, clz_optab);
- if (target)
- return target;
- break;
+ return 0;
+}
- case BUILT_IN_CTZ:
- case BUILT_IN_CTZL:
- case BUILT_IN_CTZLL:
- target = expand_builtin_unop (target_mode, arglist, target,
- subtarget, ctz_optab);
- if (target)
- return target;
- break;
+/* Expand a call to the built-in signbit, signbitf or signbitl function.
+ Return NULL_RTX if a normal call should be emitted rather than expanding
+ the function in-line. EXP is the expression that is a call to the builtin
+ function; if convenient, the result should be placed in TARGET. */
- case BUILT_IN_POPCOUNT:
- case BUILT_IN_POPCOUNTL:
- case BUILT_IN_POPCOUNTLL:
- target = expand_builtin_unop (target_mode, arglist, target,
- subtarget, popcount_optab);
- if (target)
- return target;
- break;
+static rtx
+expand_builtin_signbit (tree exp, rtx target)
+{
+ const struct real_format *fmt;
+ enum machine_mode fmode, imode, rmode;
+ HOST_WIDE_INT hi, lo;
+ tree arg, arglist;
+ int bitpos;
+ rtx temp;
+
+ arglist = TREE_OPERAND (exp, 1);
+ if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
- case BUILT_IN_PARITY:
- case BUILT_IN_PARITYL:
- case BUILT_IN_PARITYLL:
- target = expand_builtin_unop (target_mode, arglist, target,
- subtarget, parity_optab);
- if (target)
- return target;
- break;
+ arg = TREE_VALUE (arglist);
+ fmode = TYPE_MODE (TREE_TYPE (arg));
+ rmode = TYPE_MODE (TREE_TYPE (exp));
+ fmt = REAL_MODE_FORMAT (fmode);
- case BUILT_IN_STRLEN:
- target = expand_builtin_strlen (arglist, target, target_mode);
- if (target)
- return target;
- break;
+ /* For floating point formats without a sign bit, implement signbit
+ as "ARG < 0.0". */
+ if (fmt->signbit < 0)
+ {
+ /* But we can't do this if the format supports signed zero. */
+ if (fmt->has_signed_zero && HONOR_SIGNED_ZEROS (fmode))
+ return 0;
- case BUILT_IN_STRCPY:
- target = expand_builtin_strcpy (arglist, target, mode);
- if (target)
- return target;
- break;
+ arg = fold (build (LT_EXPR, TREE_TYPE (exp), arg,
+ build_real (TREE_TYPE (arg), dconst0)));
+ return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL);
+ }
- case BUILT_IN_STRNCPY:
- target = expand_builtin_strncpy (arglist, target, mode);
- if (target)
- return target;
- break;
+ imode = int_mode_for_mode (fmode);
+ if (imode == BLKmode)
+ return 0;
- case BUILT_IN_STPCPY:
- target = expand_builtin_stpcpy (arglist, target, mode);
- if (target)
- return target;
- break;
+ bitpos = fmt->signbit;
+ /* Handle targets with different FP word orders. */
+ if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+ {
+ int nwords = GET_MODE_BITSIZE (fmode) / BITS_PER_WORD;
+ int word = nwords - (bitpos / BITS_PER_WORD) - 1;
+ bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD;
+ }
- case BUILT_IN_STRCAT:
- target = expand_builtin_strcat (arglist, target, mode);
- if (target)
- return target;
- break;
+ /* If the sign bit is not in the lowpart and the floating point format
+ is wider than an integer, check that is twice the size of an integer
+ so that we can use gen_highpart below. */
+ if (bitpos >= GET_MODE_BITSIZE (rmode)
+ && GET_MODE_BITSIZE (imode) != 2 * GET_MODE_BITSIZE (rmode))
+ return 0;
- case BUILT_IN_STRNCAT:
- target = expand_builtin_strncat (arglist, target, mode);
- if (target)
- return target;
- break;
+ temp = expand_expr (arg, NULL_RTX, VOIDmode, 0);
+ temp = gen_lowpart (imode, temp);
- case BUILT_IN_STRSPN:
- target = expand_builtin_strspn (arglist, target, mode);
- if (target)
- return target;
- break;
+ if (GET_MODE_BITSIZE (imode) > GET_MODE_BITSIZE (rmode))
+ {
+ if (BITS_BIG_ENDIAN)
+ bitpos = GET_MODE_BITSIZE (imode) - 1 - bitpos;
+ temp = copy_to_mode_reg (imode, temp);
+ temp = extract_bit_field (temp, 1, bitpos, 1,
+ NULL_RTX, rmode, rmode,
+ GET_MODE_SIZE (imode));
+ }
+ else
+ {
+ if (GET_MODE_BITSIZE (imode) < GET_MODE_BITSIZE (rmode))
+ temp = gen_lowpart (rmode, temp);
+ if (bitpos < HOST_BITS_PER_WIDE_INT)
+ {
+ hi = 0;
+ lo = (HOST_WIDE_INT) 1 << bitpos;
+ }
+ else
+ {
+ hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
+ lo = 0;
+ }
- case BUILT_IN_STRCSPN:
- target = expand_builtin_strcspn (arglist, target, mode);
- if (target)
- return target;
- break;
+ temp = force_reg (rmode, temp);
+ temp = expand_binop (rmode, and_optab, temp,
+ immed_double_const (lo, hi, rmode),
+ target, 1, OPTAB_LIB_WIDEN);
+ }
+ return temp;
+}
+\f
+/* 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).
+ SUBTARGET may be used as the target for computing one of EXP's operands.
+ IGNORE is nonzero if the value is to be ignored. */
+
+rtx
+expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
+ int ignore)
+{
+ tree fndecl = get_callee_fndecl (exp);
+ tree arglist = TREE_OPERAND (exp, 1);
+ enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+ enum machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp));
+
+ /* Perform postincrements before expanding builtin functions. */
+ emit_queue ();
+
+ if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+ return targetm.expand_builtin (exp, target, subtarget, mode, ignore);
+
+ /* When not optimizing, generate calls to library functions for a certain
+ set of builtins. */
+ if (!optimize
+ && !CALLED_AS_BUILT_IN (fndecl)
+ && DECL_ASSEMBLER_NAME_SET_P (fndecl)
+ && fcode != BUILT_IN_ALLOCA)
+ return expand_call (exp, target, ignore);
+
+ /* The built-in function expanders test for target == const0_rtx
+ to determine whether the function's result will be ignored. */
+ if (ignore)
+ target = const0_rtx;
+
+ /* If the result of a pure or const built-in function is ignored, and
+ none of its arguments are volatile, we can avoid expanding the
+ built-in call and just evaluate the arguments for side-effects. */
+ if (target == const0_rtx
+ && (DECL_IS_PURE (fndecl) || TREE_READONLY (fndecl)))
+ {
+ bool volatilep = false;
+ tree arg;
+
+ for (arg = arglist; arg; arg = TREE_CHAIN (arg))
+ if (TREE_THIS_VOLATILE (TREE_VALUE (arg)))
+ {
+ volatilep = true;
+ break;
+ }
+
+ if (! volatilep)
+ {
+ for (arg = arglist; arg; arg = TREE_CHAIN (arg))
+ expand_expr (TREE_VALUE (arg), const0_rtx,
+ VOIDmode, EXPAND_NORMAL);
+ return const0_rtx;
+ }
+ }
+
+ switch (fcode)
+ {
+ case BUILT_IN_ABS:
+ case BUILT_IN_LABS:
+ case BUILT_IN_LLABS:
+ case BUILT_IN_IMAXABS:
+ /* build_function_call changes these into ABS_EXPR. */
+ abort ();
+
+ case BUILT_IN_FABS:
+ case BUILT_IN_FABSF:
+ case BUILT_IN_FABSL:
+ target = expand_builtin_fabs (arglist, target, subtarget);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_CABS:
+ case BUILT_IN_CABSF:
+ case BUILT_IN_CABSL:
+ if (flag_unsafe_math_optimizations)
+ {
+ target = expand_builtin_cabs (arglist, target);
+ if (target)
+ return target;
+ }
+ break;
+
+ case BUILT_IN_CONJ:
+ case BUILT_IN_CONJF:
+ case BUILT_IN_CONJL:
+ case BUILT_IN_CREAL:
+ case BUILT_IN_CREALF:
+ case BUILT_IN_CREALL:
+ case BUILT_IN_CIMAG:
+ case BUILT_IN_CIMAGF:
+ case BUILT_IN_CIMAGL:
+ /* expand_tree_builtin changes these into CONJ_EXPR, REALPART_EXPR
+ and IMAGPART_EXPR. */
+ abort ();
+
+ case BUILT_IN_SIN:
+ case BUILT_IN_SINF:
+ case BUILT_IN_SINL:
+ case BUILT_IN_COS:
+ case BUILT_IN_COSF:
+ case BUILT_IN_COSL:
+ case BUILT_IN_EXP:
+ case BUILT_IN_EXPF:
+ case BUILT_IN_EXPL:
+ case BUILT_IN_EXP10:
+ case BUILT_IN_EXP10F:
+ case BUILT_IN_EXP10L:
+ case BUILT_IN_POW10:
+ case BUILT_IN_POW10F:
+ case BUILT_IN_POW10L:
+ case BUILT_IN_EXP2:
+ case BUILT_IN_EXP2F:
+ case BUILT_IN_EXP2L:
+ case BUILT_IN_LOG:
+ case BUILT_IN_LOGF:
+ case BUILT_IN_LOGL:
+ case BUILT_IN_LOG10:
+ case BUILT_IN_LOG10F:
+ case BUILT_IN_LOG10L:
+ case BUILT_IN_LOG2:
+ case BUILT_IN_LOG2F:
+ case BUILT_IN_LOG2L:
+ case BUILT_IN_TAN:
+ case BUILT_IN_TANF:
+ case BUILT_IN_TANL:
+ case BUILT_IN_ATAN:
+ case BUILT_IN_ATANF:
+ case BUILT_IN_ATANL:
+ /* Treat these like sqrt only if unsafe math optimizations are allowed,
+ because of possible accuracy problems. */
+ if (! flag_unsafe_math_optimizations)
+ break;
+ case BUILT_IN_SQRT:
+ case BUILT_IN_SQRTF:
+ case BUILT_IN_SQRTL:
+ case BUILT_IN_FLOOR:
+ case BUILT_IN_FLOORF:
+ case BUILT_IN_FLOORL:
+ case BUILT_IN_CEIL:
+ case BUILT_IN_CEILF:
+ case BUILT_IN_CEILL:
+ case BUILT_IN_TRUNC:
+ case BUILT_IN_TRUNCF:
+ case BUILT_IN_TRUNCL:
+ case BUILT_IN_ROUND:
+ case BUILT_IN_ROUNDF:
+ case BUILT_IN_ROUNDL:
+ case BUILT_IN_NEARBYINT:
+ case BUILT_IN_NEARBYINTF:
+ case BUILT_IN_NEARBYINTL:
+ target = expand_builtin_mathfn (exp, target, subtarget);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_POW:
+ case BUILT_IN_POWF:
+ case BUILT_IN_POWL:
+ target = expand_builtin_pow (exp, target, subtarget);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_ATAN2:
+ case BUILT_IN_ATAN2F:
+ case BUILT_IN_ATAN2L:
+ if (! flag_unsafe_math_optimizations)
+ break;
+ target = expand_builtin_mathfn_2 (exp, target, subtarget);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_APPLY_ARGS:
+ return expand_builtin_apply_args ();
+
+ /* __builtin_apply (FUNCTION, ARGUMENTS, ARGSIZE) invokes
+ FUNCTION with a copy of the parameters described by
+ ARGUMENTS, and ARGSIZE. It returns a block of memory
+ allocated on the stack into which is stored all the registers
+ that might possibly be used for returning the result of a
+ function. ARGUMENTS is the value returned by
+ __builtin_apply_args. ARGSIZE is the number of bytes of
+ arguments that must be copied. ??? How should this value be
+ computed? We'll also need a safe worst case value for varargs
+ functions. */
+ case BUILT_IN_APPLY:
+ if (!validate_arglist (arglist, POINTER_TYPE,
+ POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)
+ && !validate_arglist (arglist, REFERENCE_TYPE,
+ POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return const0_rtx;
+ else
+ {
+ int i;
+ tree t;
+ rtx ops[3];
+
+ for (t = arglist, i = 0; t; t = TREE_CHAIN (t), i++)
+ ops[i] = expand_expr (TREE_VALUE (t), NULL_RTX, VOIDmode, 0);
+
+ return expand_builtin_apply (ops[0], ops[1], ops[2]);
+ }
+
+ /* __builtin_return (RESULT) causes the function to return the
+ value described by RESULT. RESULT is address of the block of
+ memory returned by __builtin_apply. */
+ case BUILT_IN_RETURN:
+ if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
+ expand_builtin_return (expand_expr (TREE_VALUE (arglist),
+ NULL_RTX, VOIDmode, 0));
+ return const0_rtx;
+
+ case BUILT_IN_SAVEREGS:
+ return expand_builtin_saveregs ();
+
+ case BUILT_IN_ARGS_INFO:
+ return expand_builtin_args_info (arglist);
+
+ /* Return the address of the first anonymous stack arg. */
+ case BUILT_IN_NEXT_ARG:
+ return expand_builtin_next_arg (arglist);
+
+ case BUILT_IN_CLASSIFY_TYPE:
+ return expand_builtin_classify_type (arglist);
+
+ case BUILT_IN_CONSTANT_P:
+ return expand_builtin_constant_p (arglist, target_mode);
+
+ case BUILT_IN_FRAME_ADDRESS:
+ case BUILT_IN_RETURN_ADDRESS:
+ return expand_builtin_frame_address (fndecl, arglist);
+
+ /* Returns the address of the area where the structure is returned.
+ 0 otherwise. */
+ case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
+ if (arglist != 0
+ || ! AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))
+ || GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) != MEM)
+ return const0_rtx;
+ else
+ return XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0);
+
+ case BUILT_IN_ALLOCA:
+ target = expand_builtin_alloca (arglist, target);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FFS:
+ case BUILT_IN_FFSL:
+ case BUILT_IN_FFSLL:
+ target = expand_builtin_unop (target_mode, arglist, target,
+ subtarget, ffs_optab);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_CLZ:
+ case BUILT_IN_CLZL:
+ case BUILT_IN_CLZLL:
+ target = expand_builtin_unop (target_mode, arglist, target,
+ subtarget, clz_optab);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_CTZ:
+ case BUILT_IN_CTZL:
+ case BUILT_IN_CTZLL:
+ target = expand_builtin_unop (target_mode, arglist, target,
+ subtarget, ctz_optab);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_POPCOUNT:
+ case BUILT_IN_POPCOUNTL:
+ case BUILT_IN_POPCOUNTLL:
+ target = expand_builtin_unop (target_mode, arglist, target,
+ subtarget, popcount_optab);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_PARITY:
+ case BUILT_IN_PARITYL:
+ case BUILT_IN_PARITYLL:
+ target = expand_builtin_unop (target_mode, arglist, target,
+ subtarget, parity_optab);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRLEN:
+ target = expand_builtin_strlen (arglist, target, target_mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRCPY:
+ target = expand_builtin_strcpy (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRNCPY:
+ target = expand_builtin_strncpy (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STPCPY:
+ target = expand_builtin_stpcpy (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRCAT:
+ target = expand_builtin_strcat (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRNCAT:
+ target = expand_builtin_strncat (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRSPN:
+ target = expand_builtin_strspn (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRCSPN:
+ target = expand_builtin_strcspn (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRSTR:
+ target = expand_builtin_strstr (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRPBRK:
+ target = expand_builtin_strpbrk (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_INDEX:
+ case BUILT_IN_STRCHR:
+ target = expand_builtin_strchr (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_RINDEX:
+ case BUILT_IN_STRRCHR:
+ target = expand_builtin_strrchr (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_MEMCPY:
+ target = expand_builtin_memcpy (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_MEMPCPY:
+ target = expand_builtin_mempcpy (arglist, target, mode, /*endp=*/ 1);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_MEMMOVE:
+ target = expand_builtin_memmove (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_BCOPY:
+ target = expand_builtin_bcopy (arglist);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_MEMSET:
+ target = expand_builtin_memset (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_BZERO:
+ target = expand_builtin_bzero (arglist);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRCMP:
+ target = expand_builtin_strcmp (exp, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRNCMP:
+ target = expand_builtin_strncmp (exp, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_BCMP:
+ case BUILT_IN_MEMCMP:
+ target = expand_builtin_memcmp (exp, arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_SETJMP:
+ target = expand_builtin_setjmp (arglist, target);
+ if (target)
+ return target;
+ break;
+
+ /* __builtin_longjmp is passed a pointer to an array of five words.
+ It's similar to the C library longjmp function but works with
+ __builtin_setjmp above. */
+ case BUILT_IN_LONGJMP:
+ if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ break;
+ else
+ {
+ rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget,
+ VOIDmode, 0);
+ rtx value = expand_expr (TREE_VALUE (TREE_CHAIN (arglist)),
+ NULL_RTX, VOIDmode, 0);
+
+ if (value != const1_rtx)
+ {
+ error ("__builtin_longjmp second argument must be 1");
+ return const0_rtx;
+ }
+
+ expand_builtin_longjmp (buf_addr, value);
+ return const0_rtx;
+ }
+
+ case BUILT_IN_TRAP:
+ expand_builtin_trap ();
+ return const0_rtx;
+
+ case BUILT_IN_PRINTF:
+ target = expand_builtin_printf (arglist, target, mode, false);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_PRINTF_UNLOCKED:
+ target = expand_builtin_printf (arglist, target, mode, true);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FPUTS:
+ target = expand_builtin_fputs (arglist, target, false);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FPUTS_UNLOCKED:
+ target = expand_builtin_fputs (arglist, target, true);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FPRINTF:
+ target = expand_builtin_fprintf (arglist, target, mode, false);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FPRINTF_UNLOCKED:
+ target = expand_builtin_fprintf (arglist, target, mode, true);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_SPRINTF:
+ target = expand_builtin_sprintf (arglist, target, mode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_SIGNBIT:
+ case BUILT_IN_SIGNBITF:
+ case BUILT_IN_SIGNBITL:
+ target = expand_builtin_signbit (exp, target);
+ if (target)
+ return target;
+ break;
+
+ /* Various hooks for the DWARF 2 __throw routine. */
+ case BUILT_IN_UNWIND_INIT:
+ expand_builtin_unwind_init ();
+ return const0_rtx;
+ case BUILT_IN_DWARF_CFA:
+ return virtual_cfa_rtx;
+#ifdef DWARF2_UNWIND_INFO
+ case BUILT_IN_DWARF_SP_COLUMN:
+ return expand_builtin_dwarf_sp_column ();
+ case BUILT_IN_INIT_DWARF_REG_SIZES:
+ expand_builtin_init_dwarf_reg_sizes (TREE_VALUE (arglist));
+ return const0_rtx;
+#endif
+ case BUILT_IN_FROB_RETURN_ADDR:
+ return expand_builtin_frob_return_addr (TREE_VALUE (arglist));
+ case BUILT_IN_EXTRACT_RETURN_ADDR:
+ return expand_builtin_extract_return_addr (TREE_VALUE (arglist));
+ case BUILT_IN_EH_RETURN:
+ expand_builtin_eh_return (TREE_VALUE (arglist),
+ TREE_VALUE (TREE_CHAIN (arglist)));
+ return const0_rtx;
+#ifdef EH_RETURN_DATA_REGNO
+ case BUILT_IN_EH_RETURN_DATA_REGNO:
+ return expand_builtin_eh_return_data_regno (arglist);
+#endif
+ case BUILT_IN_EXTEND_POINTER:
+ return expand_builtin_extend_pointer (TREE_VALUE (arglist));
+
+ case BUILT_IN_VA_START:
+ case BUILT_IN_STDARG_START:
+ return expand_builtin_va_start (arglist);
+ case BUILT_IN_VA_END:
+ return expand_builtin_va_end (arglist);
+ case BUILT_IN_VA_COPY:
+ return expand_builtin_va_copy (arglist);
+ case BUILT_IN_EXPECT:
+ return expand_builtin_expect (arglist, target);
+ case BUILT_IN_PREFETCH:
+ expand_builtin_prefetch (arglist);
+ return const0_rtx;
+
+
+ default: /* just do library call, if unknown builtin */
+ if (!DECL_ASSEMBLER_NAME_SET_P (fndecl))
+ error ("built-in function `%s' not currently supported",
+ IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+ }
+
+ /* The switch statement above can drop through to cause the function
+ to be called normally. */
+ return expand_call (exp, target, ignore);
+}
+
+/* Determine whether a tree node represents a call to a built-in
+ function. If the tree T is a call to a built-in function with
+ the right number of arguments of the appropriate types, return
+ the DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT.
+ Otherwise the return value is END_BUILTINS. */
+
+enum built_in_function
+builtin_mathfn_code (tree t)
+{
+ tree fndecl, arglist, parmlist;
+ tree argtype, parmtype;
+
+ if (TREE_CODE (t) != CALL_EXPR
+ || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR)
+ return END_BUILTINS;
+
+ fndecl = get_callee_fndecl (t);
+ if (fndecl == NULL_TREE
+ || TREE_CODE (fndecl) != FUNCTION_DECL
+ || ! DECL_BUILT_IN (fndecl)
+ || DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+ return END_BUILTINS;
+
+ arglist = TREE_OPERAND (t, 1);
+ parmlist = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+ for (; parmlist; parmlist = TREE_CHAIN (parmlist))
+ {
+ /* If a function doesn't take a variable number of arguments,
+ the last element in the list will have type `void'. */
+ parmtype = TREE_VALUE (parmlist);
+ if (VOID_TYPE_P (parmtype))
+ {
+ if (arglist)
+ return END_BUILTINS;
+ return DECL_FUNCTION_CODE (fndecl);
+ }
+
+ if (! arglist)
+ return END_BUILTINS;
+
+ argtype = TREE_TYPE (TREE_VALUE (arglist));
+
+ if (SCALAR_FLOAT_TYPE_P (parmtype))
+ {
+ if (! SCALAR_FLOAT_TYPE_P (argtype))
+ return END_BUILTINS;
+ }
+ else if (COMPLEX_FLOAT_TYPE_P (parmtype))
+ {
+ if (! COMPLEX_FLOAT_TYPE_P (argtype))
+ return END_BUILTINS;
+ }
+ else if (POINTER_TYPE_P (parmtype))
+ {
+ if (! POINTER_TYPE_P (argtype))
+ return END_BUILTINS;
+ }
+ else if (INTEGRAL_TYPE_P (parmtype))
+ {
+ if (! INTEGRAL_TYPE_P (argtype))
+ return END_BUILTINS;
+ }
+ else
+ return END_BUILTINS;
+
+ arglist = TREE_CHAIN (arglist);
+ }
+
+ /* Variable-length argument list. */
+ return DECL_FUNCTION_CODE (fndecl);
+}
+
+/* Fold a call to __builtin_constant_p, if we know it will evaluate to a
+ constant. ARGLIST is the argument list of the call. */
+
+static tree
+fold_builtin_constant_p (tree arglist)
+{
+ if (arglist == 0)
+ return 0;
+
+ arglist = TREE_VALUE (arglist);
+
+ /* We return 1 for a numeric type that's known to be a constant
+ value at compile-time or for an aggregate type that's a
+ literal constant. */
+ STRIP_NOPS (arglist);
+
+ /* If we know this is a constant, emit the constant of one. */
+ if (TREE_CODE_CLASS (TREE_CODE (arglist)) == 'c'
+ || (TREE_CODE (arglist) == CONSTRUCTOR
+ && TREE_CONSTANT (arglist))
+ || (TREE_CODE (arglist) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (arglist, 0)) == STRING_CST))
+ return integer_one_node;
+
+ /* If this expression has side effects, show we don't know it to be a
+ constant. Likewise if it's a pointer or aggregate type since in
+ those case we only want literals, since those are only optimized
+ when generating RTL, not later.
+ And finally, if we are compiling an initializer, not code, we
+ need to return a definite result now; there's not going to be any
+ more optimization done. */
+ if (TREE_SIDE_EFFECTS (arglist)
+ || AGGREGATE_TYPE_P (TREE_TYPE (arglist))
+ || POINTER_TYPE_P (TREE_TYPE (arglist))
+ || cfun == 0)
+ return integer_zero_node;
+
+ return 0;
+}
+
+/* Fold a call to __builtin_classify_type. */
+
+static tree
+fold_builtin_classify_type (tree arglist)
+{
+ if (arglist == 0)
+ return build_int_2 (no_type_class, 0);
+
+ return build_int_2 (type_to_class (TREE_TYPE (TREE_VALUE (arglist))), 0);
+}
+
+/* Fold a call to __builtin_inf or __builtin_huge_val. */
+
+static tree
+fold_builtin_inf (tree type, int warn)
+{
+ REAL_VALUE_TYPE real;
+
+ if (!MODE_HAS_INFINITIES (TYPE_MODE (type)) && warn)
+ warning ("target format does not support infinity");
+
+ real_inf (&real);
+ return build_real (type, real);
+}
+
+/* Fold a call to __builtin_nan or __builtin_nans. */
+
+static tree
+fold_builtin_nan (tree arglist, tree type, int quiet)
+{
+ REAL_VALUE_TYPE real;
+ const char *str;
+
+ if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
+ return 0;
+ str = c_getstr (TREE_VALUE (arglist));
+ if (!str)
+ return 0;
+
+ if (!real_nan (&real, str, quiet, TYPE_MODE (type)))
+ return 0;
+
+ return build_real (type, real);
+}
+
+/* Return true if the floating point expression T has an integer value.
+ We also allow +Inf, -Inf and NaN to be considered integer values. */
+
+static bool
+integer_valued_real_p (tree t)
+{
+ switch (TREE_CODE (t))
+ {
+ case FLOAT_EXPR:
+ return true;
+
+ case ABS_EXPR:
+ case SAVE_EXPR:
+ case NON_LVALUE_EXPR:
+ return integer_valued_real_p (TREE_OPERAND (t, 0));
+
+ case COMPOUND_EXPR:
+ case MODIFY_EXPR:
+ case BIND_EXPR:
+ return integer_valued_real_p (TREE_OPERAND (t, 1));
+
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ case MULT_EXPR:
+ case MIN_EXPR:
+ case MAX_EXPR:
+ return integer_valued_real_p (TREE_OPERAND (t, 0))
+ && integer_valued_real_p (TREE_OPERAND (t, 1));
+
+ case COND_EXPR:
+ return integer_valued_real_p (TREE_OPERAND (t, 1))
+ && integer_valued_real_p (TREE_OPERAND (t, 2));
+
+ case REAL_CST:
+ if (! TREE_CONSTANT_OVERFLOW (t))
+ {
+ REAL_VALUE_TYPE c, cint;
+
+ c = TREE_REAL_CST (t);
+ real_trunc (&cint, TYPE_MODE (TREE_TYPE (t)), &c);
+ return real_identical (&c, &cint);
+ }
+
+ case NOP_EXPR:
+ {
+ tree type = TREE_TYPE (TREE_OPERAND (t, 0));
+ if (TREE_CODE (type) == INTEGER_TYPE)
+ return true;
+ if (TREE_CODE (type) == REAL_TYPE)
+ return integer_valued_real_p (TREE_OPERAND (t, 0));
+ break;
+ }
+
+ case CALL_EXPR:
+ switch (builtin_mathfn_code (t))
+ {
+ case BUILT_IN_CEIL:
+ case BUILT_IN_CEILF:
+ case BUILT_IN_CEILL:
+ case BUILT_IN_FLOOR:
+ case BUILT_IN_FLOORF:
+ case BUILT_IN_FLOORL:
+ case BUILT_IN_NEARBYINT:
+ case BUILT_IN_NEARBYINTF:
+ case BUILT_IN_NEARBYINTL:
+ case BUILT_IN_RINT:
+ case BUILT_IN_RINTF:
+ case BUILT_IN_RINTL:
+ case BUILT_IN_ROUND:
+ case BUILT_IN_ROUNDF:
+ case BUILT_IN_ROUNDL:
+ case BUILT_IN_TRUNC:
+ case BUILT_IN_TRUNCF:
+ case BUILT_IN_TRUNCL:
+ return true;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+/* EXP is assumed to be builtin call where truncation can be propagated
+ across (for instance floor((double)f) == (double)floorf (f).
+ Do the transformation. */
+
+static tree
+fold_trunc_transparent_mathfn (tree exp)
+{
+ tree fndecl = get_callee_fndecl (exp);
+ tree arglist = TREE_OPERAND (exp, 1);
+ enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+ tree arg;
+
+ if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ arg = TREE_VALUE (arglist);
+ /* Integer rounding functions are idempotent. */
+ if (fcode == builtin_mathfn_code (arg))
+ return arg;
+
+ /* If argument is already integer valued, and we don't need to worry
+ about setting errno, there's no need to perform rounding. */
+ if (! flag_errno_math && integer_valued_real_p (arg))
+ return arg;
+
+ if (optimize)
+ {
+ tree arg0 = strip_float_extensions (arg);
+ tree ftype = TREE_TYPE (exp);
+ tree newtype = TREE_TYPE (arg0);
+ tree decl;
+
+ if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype)
+ && (decl = mathfn_built_in (newtype, fcode)))
+ {
+ arglist =
+ build_tree_list (NULL_TREE, fold (convert (newtype, arg0)));
+ return convert (ftype,
+ build_function_call_expr (decl, arglist));
+ }
+ }
+ return 0;
+}
+
+/* Fold function call to builtin cabs, cabsf or cabsl. FNDECL is the
+ function's DECL, ARGLIST is the argument list and TYPE is the return
+ type. Return NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_cabs (tree fndecl, tree arglist, tree type)
+{
+ tree arg;
+
+ if (!arglist || TREE_CHAIN (arglist))
+ return NULL_TREE;
+
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (arg)) != COMPLEX_TYPE
+ || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != REAL_TYPE)
+ return NULL_TREE;
+
+ /* Evaluate cabs of a constant at compile-time. */
+ if (flag_unsafe_math_optimizations
+ && TREE_CODE (arg) == COMPLEX_CST
+ && TREE_CODE (TREE_REALPART (arg)) == REAL_CST
+ && TREE_CODE (TREE_IMAGPART (arg)) == REAL_CST
+ && ! TREE_CONSTANT_OVERFLOW (TREE_REALPART (arg))
+ && ! TREE_CONSTANT_OVERFLOW (TREE_IMAGPART (arg)))
+ {
+ REAL_VALUE_TYPE r, i;
+
+ r = TREE_REAL_CST (TREE_REALPART (arg));
+ i = TREE_REAL_CST (TREE_IMAGPART (arg));
+
+ real_arithmetic (&r, MULT_EXPR, &r, &r);
+ real_arithmetic (&i, MULT_EXPR, &i, &i);
+ real_arithmetic (&r, PLUS_EXPR, &r, &i);
+ if (real_sqrt (&r, TYPE_MODE (type), &r)
+ || ! flag_trapping_math)
+ return build_real (type, r);
+ }
+
+ /* If either part is zero, cabs is fabs of the other. */
+ if (TREE_CODE (arg) == COMPLEX_EXPR
+ && real_zerop (TREE_OPERAND (arg, 0)))
+ return fold (build1 (ABS_EXPR, type, TREE_OPERAND (arg, 1)));
+ if (TREE_CODE (arg) == COMPLEX_EXPR
+ && real_zerop (TREE_OPERAND (arg, 1)))
+ return fold (build1 (ABS_EXPR, type, TREE_OPERAND (arg, 0)));
+
+ if (flag_unsafe_math_optimizations)
+ {
+ enum built_in_function fcode;
+ tree sqrtfn;
+
+ fcode = DECL_FUNCTION_CODE (fndecl);
+ if (fcode == BUILT_IN_CABS)
+ sqrtfn = implicit_built_in_decls[BUILT_IN_SQRT];
+ else if (fcode == BUILT_IN_CABSF)
+ sqrtfn = implicit_built_in_decls[BUILT_IN_SQRTF];
+ else if (fcode == BUILT_IN_CABSL)
+ sqrtfn = implicit_built_in_decls[BUILT_IN_SQRTL];
+ else
+ sqrtfn = NULL_TREE;
+
+ if (sqrtfn != NULL_TREE)
+ {
+ tree rpart, ipart, result, arglist;
+
+ arg = save_expr (arg);
+
+ rpart = fold (build1 (REALPART_EXPR, type, arg));
+ ipart = fold (build1 (IMAGPART_EXPR, type, arg));
+
+ rpart = save_expr (rpart);
+ ipart = save_expr (ipart);
+
+ result = fold (build (PLUS_EXPR, type,
+ fold (build (MULT_EXPR, type,
+ rpart, rpart)),
+ fold (build (MULT_EXPR, type,
+ ipart, ipart))));
+
+ arglist = build_tree_list (NULL_TREE, result);
+ return build_function_call_expr (sqrtfn, arglist);
+ }
+ }
+
+ return NULL_TREE;
+}
+
+/* Fold function call to builtin trunc, truncf or truncl. Return
+ NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_trunc (tree exp)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg;
+
+ if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ /* Optimize trunc of constant value. */
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ REAL_VALUE_TYPE r, x;
+ tree type = TREE_TYPE (exp);
+
+ x = TREE_REAL_CST (arg);
+ real_trunc (&r, TYPE_MODE (type), &x);
+ return build_real (type, r);
+ }
+
+ return fold_trunc_transparent_mathfn (exp);
+}
+
+/* Fold function call to builtin floor, floorf or floorl. Return
+ NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_floor (tree exp)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg;
+
+ if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ /* Optimize floor of constant value. */
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ REAL_VALUE_TYPE x;
+
+ x = TREE_REAL_CST (arg);
+ if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
+ {
+ tree type = TREE_TYPE (exp);
+ REAL_VALUE_TYPE r;
+
+ real_floor (&r, TYPE_MODE (type), &x);
+ return build_real (type, r);
+ }
+ }
+
+ return fold_trunc_transparent_mathfn (exp);
+}
+
+/* Fold function call to builtin ceil, ceilf or ceill. Return
+ NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_ceil (tree exp)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg;
+
+ if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ /* Optimize ceil of constant value. */
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ REAL_VALUE_TYPE x;
+
+ x = TREE_REAL_CST (arg);
+ if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
+ {
+ tree type = TREE_TYPE (exp);
+ REAL_VALUE_TYPE r;
+
+ real_ceil (&r, TYPE_MODE (type), &x);
+ return build_real (type, r);
+ }
+ }
+
+ return fold_trunc_transparent_mathfn (exp);
+}
+
+/* Fold function call to builtin round, roundf or roundl. Return
+ NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_round (tree exp)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg;
+
+ if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ /* Optimize ceil of constant value. */
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ REAL_VALUE_TYPE x;
+
+ x = TREE_REAL_CST (arg);
+ if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
+ {
+ tree type = TREE_TYPE (exp);
+ REAL_VALUE_TYPE r;
+
+ real_round (&r, TYPE_MODE (type), &x);
+ return build_real (type, r);
+ }
+ }
+
+ return fold_trunc_transparent_mathfn (exp);
+}
+
+/* Fold function call to builtin ffs, clz, ctz, popcount and parity
+ and their long and long long variants (i.e. ffsl and ffsll).
+ Return NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_bitop (tree exp)
+{
+ tree fndecl = get_callee_fndecl (exp);
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg;
+
+ if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
+ return NULL_TREE;
+
+ /* Optimize for constant argument. */
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) == INTEGER_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ HOST_WIDE_INT hi, width, result;
+ unsigned HOST_WIDE_INT lo;
+ tree type, t;
+
+ type = TREE_TYPE (arg);
+ width = TYPE_PRECISION (type);
+ lo = TREE_INT_CST_LOW (arg);
+
+ /* Clear all the bits that are beyond the type's precision. */
+ if (width > HOST_BITS_PER_WIDE_INT)
+ {
+ hi = TREE_INT_CST_HIGH (arg);
+ if (width < 2 * HOST_BITS_PER_WIDE_INT)
+ hi &= ~((HOST_WIDE_INT) (-1) >> (width - HOST_BITS_PER_WIDE_INT));
+ }
+ else
+ {
+ hi = 0;
+ if (width < HOST_BITS_PER_WIDE_INT)
+ lo &= ~((unsigned HOST_WIDE_INT) (-1) << width);
+ }
+
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_FFS:
+ case BUILT_IN_FFSL:
+ case BUILT_IN_FFSLL:
+ if (lo != 0)
+ result = exact_log2 (lo & -lo) + 1;
+ else if (hi != 0)
+ result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi) + 1;
+ else
+ result = 0;
+ break;
+
+ case BUILT_IN_CLZ:
+ case BUILT_IN_CLZL:
+ case BUILT_IN_CLZLL:
+ if (hi != 0)
+ result = width - floor_log2 (hi) - 1 - HOST_BITS_PER_WIDE_INT;
+ else if (lo != 0)
+ result = width - floor_log2 (lo) - 1;
+ else if (! CLZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result))
+ result = width;
+ break;
+
+ case BUILT_IN_CTZ:
+ case BUILT_IN_CTZL:
+ case BUILT_IN_CTZLL:
+ if (lo != 0)
+ result = exact_log2 (lo & -lo);
+ else if (hi != 0)
+ result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi);
+ else if (! CTZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result))
+ result = width;
+ break;
+
+ case BUILT_IN_POPCOUNT:
+ case BUILT_IN_POPCOUNTL:
+ case BUILT_IN_POPCOUNTLL:
+ result = 0;
+ while (lo)
+ result++, lo &= lo - 1;
+ while (hi)
+ result++, hi &= hi - 1;
+ break;
+
+ case BUILT_IN_PARITY:
+ case BUILT_IN_PARITYL:
+ case BUILT_IN_PARITYLL:
+ result = 0;
+ while (lo)
+ result++, lo &= lo - 1;
+ while (hi)
+ result++, hi &= hi - 1;
+ result &= 1;
+ break;
+
+ default:
+ abort();
+ }
+
+ t = build_int_2 (result, 0);
+ TREE_TYPE (t) = TREE_TYPE (exp);
+ return t;
+ }
- case BUILT_IN_STRSTR:
- target = expand_builtin_strstr (arglist, target, mode);
- if (target)
- return target;
- break;
+ return NULL_TREE;
+}
- case BUILT_IN_STRPBRK:
- target = expand_builtin_strpbrk (arglist, target, mode);
- if (target)
- return target;
- break;
+/* Return true if EXPR is the real constant contained in VALUE. */
- case BUILT_IN_INDEX:
- case BUILT_IN_STRCHR:
- target = expand_builtin_strchr (arglist, target, mode);
- if (target)
- return target;
- break;
+static bool
+real_dconstp (tree expr, const REAL_VALUE_TYPE *value)
+{
+ STRIP_NOPS (expr);
+
+ return ((TREE_CODE (expr) == REAL_CST
+ && ! TREE_CONSTANT_OVERFLOW (expr)
+ && REAL_VALUES_EQUAL (TREE_REAL_CST (expr), *value))
+ || (TREE_CODE (expr) == COMPLEX_CST
+ && real_dconstp (TREE_REALPART (expr), value)
+ && real_zerop (TREE_IMAGPART (expr))));
+}
- case BUILT_IN_RINDEX:
- case BUILT_IN_STRRCHR:
- target = expand_builtin_strrchr (arglist, target, mode);
- if (target)
- return target;
- break;
+/* A subroutine of fold_builtin to fold the various logarithmic
+ functions. EXP is the CALL_EXPR of a call to a builtin logN
+ function. VALUE is the base of the logN function. */
- case BUILT_IN_MEMCPY:
- target = expand_builtin_memcpy (arglist, target, mode, /*endp=*/0);
- if (target)
- return target;
- break;
+static tree
+fold_builtin_logarithm (tree exp, const REAL_VALUE_TYPE *value)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
- case BUILT_IN_MEMPCPY:
- target = expand_builtin_mempcpy (arglist, target, mode);
- if (target)
- return target;
- break;
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ tree fndecl = get_callee_fndecl (exp);
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
+ tree arg = TREE_VALUE (arglist);
+ const enum built_in_function fcode = builtin_mathfn_code (arg);
- case BUILT_IN_MEMMOVE:
- target = expand_builtin_memmove (arglist, target, mode);
- if (target)
- return target;
- break;
+ /* Optimize logN(1.0) = 0.0. */
+ if (real_onep (arg))
+ return build_real (type, dconst0);
- case BUILT_IN_BCOPY:
- target = expand_builtin_bcopy (arglist);
- if (target)
- return target;
- break;
+ /* Optimize logN(N) = 1.0. If N can't be truncated to MODE
+ exactly, then only do this if flag_unsafe_math_optimizations. */
+ if (exact_real_truncate (TYPE_MODE (type), value)
+ || flag_unsafe_math_optimizations)
+ {
+ const REAL_VALUE_TYPE value_truncate =
+ real_value_truncate (TYPE_MODE (type), *value);
+ if (real_dconstp (arg, &value_truncate))
+ return build_real (type, dconst1);
+ }
- case BUILT_IN_MEMSET:
- target = expand_builtin_memset (arglist, target, mode);
- if (target)
- return target;
- break;
+ /* Special case, optimize logN(expN(x)) = x. */
+ if (flag_unsafe_math_optimizations
+ && ((value == &dconste
+ && (fcode == BUILT_IN_EXP
+ || fcode == BUILT_IN_EXPF
+ || fcode == BUILT_IN_EXPL))
+ || (value == &dconst2
+ && (fcode == BUILT_IN_EXP2
+ || fcode == BUILT_IN_EXP2F
+ || fcode == BUILT_IN_EXP2L))
+ || (value == &dconst10 && (BUILTIN_EXP10_P (fcode)))))
+ return convert (type, TREE_VALUE (TREE_OPERAND (arg, 1)));
+
+ /* Optimize logN(func()) for various exponential functions. We
+ want to determine the value "x" and the power "exponent" in
+ order to transform logN(x**exponent) into exponent*logN(x). */
+ if (flag_unsafe_math_optimizations)
+ {
+ tree exponent = 0, x = 0;
- case BUILT_IN_BZERO:
- target = expand_builtin_bzero (arglist);
- if (target)
- return target;
- break;
+ switch (fcode)
+ {
+ case BUILT_IN_EXP:
+ case BUILT_IN_EXPF:
+ case BUILT_IN_EXPL:
+ /* Prepare to do logN(exp(exponent) -> exponent*logN(e). */
+ x = build_real (type,
+ real_value_truncate (TYPE_MODE (type), dconste));
+ exponent = TREE_VALUE (TREE_OPERAND (arg, 1));
+ break;
+ case BUILT_IN_EXP2:
+ case BUILT_IN_EXP2F:
+ case BUILT_IN_EXP2L:
+ /* Prepare to do logN(exp2(exponent) -> exponent*logN(2). */
+ x = build_real (type, dconst2);
+ exponent = TREE_VALUE (TREE_OPERAND (arg, 1));
+ break;
+ case BUILT_IN_EXP10:
+ case BUILT_IN_EXP10F:
+ case BUILT_IN_EXP10L:
+ case BUILT_IN_POW10:
+ case BUILT_IN_POW10F:
+ case BUILT_IN_POW10L:
+ /* Prepare to do logN(exp10(exponent) -> exponent*logN(10). */
+ x = build_real (type, dconst10);
+ exponent = TREE_VALUE (TREE_OPERAND (arg, 1));
+ break;
+ case BUILT_IN_SQRT:
+ case BUILT_IN_SQRTF:
+ case BUILT_IN_SQRTL:
+ /* Prepare to do logN(sqrt(x) -> 0.5*logN(x). */
+ x = TREE_VALUE (TREE_OPERAND (arg, 1));
+ exponent = build_real (type, dconsthalf);
+ break;
+ case BUILT_IN_CBRT:
+ case BUILT_IN_CBRTF:
+ case BUILT_IN_CBRTL:
+ /* Prepare to do logN(cbrt(x) -> (1/3)*logN(x). */
+ x = TREE_VALUE (TREE_OPERAND (arg, 1));
+ exponent = build_real (type, real_value_truncate (TYPE_MODE (type),
+ dconstthird));
+ break;
+ case BUILT_IN_POW:
+ case BUILT_IN_POWF:
+ case BUILT_IN_POWL:
+ /* Prepare to do logN(pow(x,exponent) -> exponent*logN(x). */
+ x = TREE_VALUE (TREE_OPERAND (arg, 1));
+ exponent = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1)));
+ break;
+ default:
+ break;
+ }
- case BUILT_IN_STRCMP:
- target = expand_builtin_strcmp (exp, target, mode);
- if (target)
- return target;
- break;
+ /* Now perform the optimization. */
+ if (x && exponent)
+ {
+ tree logfn;
+ arglist = build_tree_list (NULL_TREE, x);
+ logfn = build_function_call_expr (fndecl, arglist);
+ return fold (build (MULT_EXPR, type, exponent, logfn));
+ }
+ }
+ }
- case BUILT_IN_STRNCMP:
- target = expand_builtin_strncmp (exp, target, mode);
- if (target)
- return target;
- break;
+ return 0;
+}
- case BUILT_IN_BCMP:
- case BUILT_IN_MEMCMP:
- target = expand_builtin_memcmp (exp, arglist, target, mode);
- if (target)
- return target;
- break;
+/* A subroutine of fold_builtin to fold the various exponent
+ functions. EXP is the CALL_EXPR of a call to a builtin function.
+ VALUE is the value which will be raised to a power. */
- case BUILT_IN_SETJMP:
- target = expand_builtin_setjmp (arglist, target);
- if (target)
- return target;
- break;
+static tree
+fold_builtin_exponent (tree exp, const REAL_VALUE_TYPE *value)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
- /* __builtin_longjmp is passed a pointer to an array of five words.
- It's similar to the C library longjmp function but works with
- __builtin_setjmp above. */
- case BUILT_IN_LONGJMP:
- if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
- break;
- else
- {
- rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget,
- VOIDmode, 0);
- rtx value = expand_expr (TREE_VALUE (TREE_CHAIN (arglist)),
- NULL_RTX, VOIDmode, 0);
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ tree fndecl = get_callee_fndecl (exp);
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
+ tree arg = TREE_VALUE (arglist);
- if (value != const1_rtx)
+ /* Optimize exp*(0.0) = 1.0. */
+ if (real_zerop (arg))
+ return build_real (type, dconst1);
+
+ /* Optimize expN(1.0) = N. */
+ if (real_onep (arg))
+ {
+ REAL_VALUE_TYPE cst;
+
+ real_convert (&cst, TYPE_MODE (type), value);
+ return build_real (type, cst);
+ }
+
+ /* Attempt to evaluate expN(integer) at compile-time. */
+ if (flag_unsafe_math_optimizations
+ && TREE_CODE (arg) == REAL_CST
+ && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ REAL_VALUE_TYPE cint;
+ REAL_VALUE_TYPE c;
+ HOST_WIDE_INT n;
+
+ c = TREE_REAL_CST (arg);
+ n = real_to_integer (&c);
+ real_from_integer (&cint, VOIDmode, n,
+ n < 0 ? -1 : 0, 0);
+ if (real_identical (&c, &cint))
{
- error ("__builtin_longjmp second argument must be 1");
- return const0_rtx;
+ REAL_VALUE_TYPE x;
+
+ real_powi (&x, TYPE_MODE (type), value, n);
+ return build_real (type, x);
}
+ }
- expand_builtin_longjmp (buf_addr, value);
- return const0_rtx;
+ /* Optimize expN(logN(x)) = x. */
+ if (flag_unsafe_math_optimizations)
+ {
+ const enum built_in_function fcode = builtin_mathfn_code (arg);
+
+ if ((value == &dconste
+ && (fcode == BUILT_IN_LOG
+ || fcode == BUILT_IN_LOGF
+ || fcode == BUILT_IN_LOGL))
+ || (value == &dconst2
+ && (fcode == BUILT_IN_LOG2
+ || fcode == BUILT_IN_LOG2F
+ || fcode == BUILT_IN_LOG2L))
+ || (value == &dconst10
+ && (fcode == BUILT_IN_LOG10
+ || fcode == BUILT_IN_LOG10F
+ || fcode == BUILT_IN_LOG10L)))
+ return convert (type, TREE_VALUE (TREE_OPERAND (arg, 1)));
}
+ }
- case BUILT_IN_TRAP:
- expand_builtin_trap ();
- return const0_rtx;
+ return 0;
+}
- case BUILT_IN_FPUTS:
- target = expand_builtin_fputs (arglist, ignore,/*unlocked=*/ 0);
- if (target)
- return target;
- break;
- case BUILT_IN_FPUTS_UNLOCKED:
- target = expand_builtin_fputs (arglist, ignore,/*unlocked=*/ 1);
- if (target)
- return target;
- break;
+/* Fold function call to builtin memcpy. Return
+ NULL_TREE if no simplification can be made. */
- /* Various hooks for the DWARF 2 __throw routine. */
- case BUILT_IN_UNWIND_INIT:
- expand_builtin_unwind_init ();
- return const0_rtx;
- case BUILT_IN_DWARF_CFA:
- return virtual_cfa_rtx;
-#ifdef DWARF2_UNWIND_INFO
- case BUILT_IN_DWARF_SP_COLUMN:
- return expand_builtin_dwarf_sp_column ();
- case BUILT_IN_INIT_DWARF_REG_SIZES:
- expand_builtin_init_dwarf_reg_sizes (TREE_VALUE (arglist));
- return const0_rtx;
-#endif
- case BUILT_IN_FROB_RETURN_ADDR:
- return expand_builtin_frob_return_addr (TREE_VALUE (arglist));
- case BUILT_IN_EXTRACT_RETURN_ADDR:
- return expand_builtin_extract_return_addr (TREE_VALUE (arglist));
- case BUILT_IN_EH_RETURN:
- expand_builtin_eh_return (TREE_VALUE (arglist),
- TREE_VALUE (TREE_CHAIN (arglist)));
- return const0_rtx;
-#ifdef EH_RETURN_DATA_REGNO
- case BUILT_IN_EH_RETURN_DATA_REGNO:
- return expand_builtin_eh_return_data_regno (arglist);
-#endif
- case BUILT_IN_VA_START:
- case BUILT_IN_STDARG_START:
- return expand_builtin_va_start (arglist);
- case BUILT_IN_VA_END:
- return expand_builtin_va_end (arglist);
- case BUILT_IN_VA_COPY:
- return expand_builtin_va_copy (arglist);
- case BUILT_IN_EXPECT:
- return expand_builtin_expect (arglist, target);
- case BUILT_IN_PREFETCH:
- expand_builtin_prefetch (arglist);
- return const0_rtx;
+static tree
+fold_builtin_memcpy (tree exp)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree dest, src, len;
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
- default: /* just do library call, if unknown builtin */
- if (!DECL_ASSEMBLER_NAME_SET_P (fndecl))
- error ("built-in function `%s' not currently supported",
- IDENTIFIER_POINTER (DECL_NAME (fndecl)));
- }
+ dest = TREE_VALUE (arglist);
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
- /* The switch statement above can drop through to cause the function
- to be called normally. */
- return expand_call (exp, target, ignore);
+ /* If the LEN parameter is zero, return DEST. */
+ if (integer_zerop (len))
+ return omit_one_operand (TREE_TYPE (exp), dest, src);
+
+ /* If SRC and DEST are the same (and not volatile), return DEST. */
+ if (operand_equal_p (src, dest, 0))
+ return omit_one_operand (TREE_TYPE (exp), dest, len);
+
+ return 0;
}
-/* Determine whether a tree node represents a call to a built-in
- math function. If the tree T is a call to a built-in function
- taking a single real argument, then the return value is the
- DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT. Otherwise
- the return value is END_BUILTINS. */
-
-enum built_in_function
-builtin_mathfn_code (t)
- tree t;
+/* Fold function call to builtin mempcpy. Return
+ NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_mempcpy (tree exp)
{
- tree fndecl, arglist;
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree dest, src, len;
- if (TREE_CODE (t) != CALL_EXPR
- || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR)
- return END_BUILTINS;
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
- fndecl = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
- if (TREE_CODE (fndecl) != FUNCTION_DECL
- || ! DECL_BUILT_IN (fndecl)
- || DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
- return END_BUILTINS;
+ dest = TREE_VALUE (arglist);
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
- arglist = TREE_OPERAND (t, 1);
- if (! arglist
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE)
- return END_BUILTINS;
+ /* If the LEN parameter is zero, return DEST. */
+ if (integer_zerop (len))
+ return omit_one_operand (TREE_TYPE (exp), dest, src);
- arglist = TREE_CHAIN (arglist);
- switch (DECL_FUNCTION_CODE (fndecl))
+ /* If SRC and DEST are the same (and not volatile), return DEST+LEN. */
+ if (operand_equal_p (src, dest, 0))
{
- case BUILT_IN_POW:
- case BUILT_IN_POWF:
- case BUILT_IN_POWL:
- case BUILT_IN_ATAN2:
- case BUILT_IN_ATAN2F:
- case BUILT_IN_ATAN2L:
- if (! arglist
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE
- || TREE_CHAIN (arglist))
- return END_BUILTINS;
- break;
-
- default:
- if (arglist)
- return END_BUILTINS;
- break;
+ tree temp = convert (TREE_TYPE (dest), len);
+ temp = fold (build (PLUS_EXPR, TREE_TYPE (dest), dest, len));
+ return convert (TREE_TYPE (exp), temp);
}
- return DECL_FUNCTION_CODE (fndecl);
+ return 0;
+}
+
+/* Fold function call to builtin memmove. Return
+ NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_memmove (tree exp)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree dest, src, len;
+
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ dest = TREE_VALUE (arglist);
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+ /* If the LEN parameter is zero, return DEST. */
+ if (integer_zerop (len))
+ return omit_one_operand (TREE_TYPE (exp), dest, src);
+
+ /* If SRC and DEST are the same (and not volatile), return DEST. */
+ if (operand_equal_p (src, dest, 0))
+ return omit_one_operand (TREE_TYPE (exp), dest, len);
+
+ return 0;
}
-/* Fold a call to __builtin_constant_p, if we know it will evaluate to a
- constant. ARGLIST is the argument list of the call. */
+/* Fold function call to builtin strcpy. Return
+ NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_constant_p (arglist)
- tree arglist;
+fold_builtin_strcpy (tree exp)
{
- if (arglist == 0)
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree dest, src;
+
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
- arglist = TREE_VALUE (arglist);
+ dest = TREE_VALUE (arglist);
+ src = TREE_VALUE (TREE_CHAIN (arglist));
- /* We return 1 for a numeric type that's known to be a constant
- value at compile-time or for an aggregate type that's a
- literal constant. */
- STRIP_NOPS (arglist);
+ /* If SRC and DEST are the same (and not volatile), return DEST. */
+ if (operand_equal_p (src, dest, 0))
+ return convert (TREE_TYPE (exp), dest);
- /* If we know this is a constant, emit the constant of one. */
- if (TREE_CODE_CLASS (TREE_CODE (arglist)) == 'c'
- || (TREE_CODE (arglist) == CONSTRUCTOR
- && TREE_CONSTANT (arglist))
- || (TREE_CODE (arglist) == ADDR_EXPR
- && TREE_CODE (TREE_OPERAND (arglist, 0)) == STRING_CST))
- return integer_one_node;
+ return 0;
+}
- /* If we aren't going to be running CSE or this expression
- has side effects, show we don't know it to be a constant.
- Likewise if it's a pointer or aggregate type since in those
- case we only want literals, since those are only optimized
- when generating RTL, not later.
- And finally, if we are compiling an initializer, not code, we
- need to return a definite result now; there's not going to be any
- more optimization done. */
- if (TREE_SIDE_EFFECTS (arglist) || cse_not_expected
- || AGGREGATE_TYPE_P (TREE_TYPE (arglist))
- || POINTER_TYPE_P (TREE_TYPE (arglist))
- || cfun == 0)
- return integer_zero_node;
+/* Fold function call to builtin strncpy. Return
+ NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_strncpy (tree exp)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree dest, src, len;
+
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ dest = TREE_VALUE (arglist);
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+ /* If the LEN parameter is zero, return DEST. */
+ if (integer_zerop (len))
+ return omit_one_operand (TREE_TYPE (exp), dest, src);
return 0;
}
-/* Fold a call to __builtin_classify_type. */
+/* Fold function call to builtin memcmp. Return
+ NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_classify_type (arglist)
- tree arglist;
+fold_builtin_memcmp (tree exp)
{
- if (arglist == 0)
- return build_int_2 (no_type_class, 0);
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg1, arg2, len;
- return build_int_2 (type_to_class (TREE_TYPE (TREE_VALUE (arglist))), 0);
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ arg1 = TREE_VALUE (arglist);
+ arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+ len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+ /* If the LEN parameter is zero, return zero. */
+ if (integer_zerop (len))
+ {
+ tree temp = omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg2);
+ return omit_one_operand (TREE_TYPE (exp), temp, arg1);
+ }
+
+ /* If ARG1 and ARG2 are the same (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ return omit_one_operand (TREE_TYPE (exp), integer_zero_node, len);
+
+ return 0;
}
-/* Fold a call to __builtin_inf or __builtin_huge_val. */
+/* Fold function call to builtin strcmp. Return
+ NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_inf (type, warn)
- tree type;
- int warn;
+fold_builtin_strcmp (tree exp)
{
- REAL_VALUE_TYPE real;
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg1, arg2;
+ const char *p1, *p2;
- if (!MODE_HAS_INFINITIES (TYPE_MODE (type)) && warn)
- warning ("target format does not support infinity");
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+ return 0;
- real_inf (&real);
- return build_real (type, real);
+ arg1 = TREE_VALUE (arglist);
+ arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+
+ /* If ARG1 and ARG2 are the same (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ return convert (TREE_TYPE (exp), integer_zero_node);
+
+ p1 = c_getstr (arg1);
+ p2 = c_getstr (arg2);
+
+ if (p1 && p2)
+ {
+ tree temp;
+ const int i = strcmp (p1, p2);
+ if (i < 0)
+ temp = integer_minus_one_node;
+ else if (i > 0)
+ temp = integer_one_node;
+ else
+ temp = integer_zero_node;
+ return convert (TREE_TYPE (exp), temp);
+ }
+
+ return 0;
}
-/* Fold a call to __builtin_nan or __builtin_nans. */
+/* Fold function call to builtin strncmp. Return
+ NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_nan (arglist, type, quiet)
- tree arglist, type;
- int quiet;
+fold_builtin_strncmp (tree exp)
{
- REAL_VALUE_TYPE real;
- const char *str;
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg1, arg2, len;
+ const char *p1, *p2;
- if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
- return 0;
- str = c_getstr (TREE_VALUE (arglist));
- if (!str)
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
- if (!real_nan (&real, str, quiet, TYPE_MODE (type)))
- return 0;
+ arg1 = TREE_VALUE (arglist);
+ arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+ len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
- return build_real (type, real);
+ /* If the LEN parameter is zero, return zero. */
+ if (integer_zerop (len))
+ {
+ tree temp = omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg2);
+ return omit_one_operand (TREE_TYPE (exp), temp, arg1);
+ }
+
+ /* If ARG1 and ARG2 are the same (and not volatile), return zero. */
+ if (operand_equal_p (arg1, arg2, 0))
+ return omit_one_operand (TREE_TYPE (exp), integer_zero_node, len);
+
+ p1 = c_getstr (arg1);
+ p2 = c_getstr (arg2);
+
+ if (host_integerp (len, 1) && p1 && p2)
+ {
+ tree temp;
+ const int i = strncmp (p1, p2, tree_low_cst (len, 1));
+ if (i < 0)
+ temp = integer_minus_one_node;
+ else if (i > 0)
+ temp = integer_one_node;
+ else
+ temp = integer_zero_node;
+ return convert (TREE_TYPE (exp), temp);
+ }
+
+ return 0;
}
-/* EXP is assumed to me builtin call where truncation can be propagated
- across (for instance floor((double)f) == (double)floorf (f).
- Do the transformation. */
+/* Fold function call to builtin signbit, signbitf or signbitl. Return
+ NULL_TREE if no simplification can be made. */
+
static tree
-fold_trunc_transparent_mathfn (exp)
- tree exp;
+fold_builtin_signbit (tree exp)
{
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
tree arglist = TREE_OPERAND (exp, 1);
- enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+ tree arg, temp;
+
+ if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return NULL_TREE;
+
+ arg = TREE_VALUE (arglist);
- if (optimize && validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ /* If ARG is a compile-time constant, determine the result. */
+ if (TREE_CODE (arg) == REAL_CST
+ && !TREE_CONSTANT_OVERFLOW (arg))
{
- tree arg0 = strip_float_extensions (TREE_VALUE (arglist));
- tree ftype = TREE_TYPE (exp);
- tree newtype = TREE_TYPE (arg0);
- tree decl;
+ REAL_VALUE_TYPE c;
- if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype)
- && (decl = mathfn_built_in (newtype, fcode)))
- {
- arglist =
- build_tree_list (NULL_TREE, fold (convert (newtype, arg0)));
- return convert (ftype,
- build_function_call_expr (decl, arglist));
- }
+ c = TREE_REAL_CST (arg);
+ temp = REAL_VALUE_NEGATIVE (c) ? integer_one_node : integer_zero_node;
+ return convert (TREE_TYPE (exp), temp);
}
- return 0;
+
+ /* If ARG is non-negative, the result is always zero. */
+ if (tree_expr_nonnegative_p (arg))
+ return omit_one_operand (TREE_TYPE (exp), integer_zero_node, arg);
+
+ /* If ARG's format doesn't have signed zeros, return "arg < 0.0". */
+ if (!HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg))))
+ return fold (build (LT_EXPR, TREE_TYPE (exp), arg,
+ build_real (TREE_TYPE (arg), dconst0)));
+
+ return NULL_TREE;
}
+
/* Used by constant folding to eliminate some builtin calls early. EXP is
the CALL_EXPR of a call to a builtin function. */
tree
-fold_builtin (exp)
- tree exp;
+fold_builtin (tree exp)
{
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree fndecl = get_callee_fndecl (exp);
tree arglist = TREE_OPERAND (exp, 1);
tree type = TREE_TYPE (TREE_TYPE (fndecl));
case BUILT_IN_STRLEN:
if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
{
- tree len = c_strlen (TREE_VALUE (arglist));
+ tree len = c_strlen (TREE_VALUE (arglist), 0);
if (len)
{
/* Convert from the internal "sizetype" type to "size_t". */
}
break;
+ case BUILT_IN_FABS:
+ case BUILT_IN_FABSF:
+ case BUILT_IN_FABSL:
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return fold (build1 (ABS_EXPR, type, TREE_VALUE (arglist)));
+ break;
+
+ case BUILT_IN_CABS:
+ case BUILT_IN_CABSF:
+ case BUILT_IN_CABSL:
+ return fold_builtin_cabs (fndecl, arglist, type);
+
case BUILT_IN_SQRT:
case BUILT_IN_SQRTF:
case BUILT_IN_SQRTL:
return build_real (type, r);
}
- /* Optimize sqrt(exp(x)) = exp(x*0.5). */
+ /* Optimize sqrt(expN(x)) = expN(x*0.5). */
fcode = builtin_mathfn_code (arg);
- if (flag_unsafe_math_optimizations
- && (fcode == BUILT_IN_EXP
- || fcode == BUILT_IN_EXPF
- || fcode == BUILT_IN_EXPL))
+ if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode))
{
tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
arg = fold (build (MULT_EXPR, type,
return build_function_call_expr (expfn, arglist);
}
+ /* Optimize sqrt(Nroot(x)) -> pow(x,1/(2*N)). */
+ if (flag_unsafe_math_optimizations && BUILTIN_ROOT_P (fcode))
+ {
+ tree powfn = mathfn_built_in (type, BUILT_IN_POW);
+
+ if (powfn)
+ {
+ tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
+ tree tree_root;
+ /* The inner root was either sqrt or cbrt. */
+ REAL_VALUE_TYPE dconstroot =
+ BUILTIN_SQRT_P (fcode) ? dconsthalf : dconstthird;
+
+ /* Adjust for the outer root. */
+ SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1);
+ dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
+ tree_root = build_real (type, dconstroot);
+ arglist = tree_cons (NULL_TREE, arg0,
+ build_tree_list (NULL_TREE, tree_root));
+ return build_function_call_expr (powfn, arglist);
+ }
+ }
+
/* Optimize sqrt(pow(x,y)) = pow(x,y*0.5). */
if (flag_unsafe_math_optimizations
&& (fcode == BUILT_IN_POW
}
break;
+ case BUILT_IN_CBRT:
+ case BUILT_IN_CBRTF:
+ case BUILT_IN_CBRTL:
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ tree arg = TREE_VALUE (arglist);
+ const enum built_in_function fcode = builtin_mathfn_code (arg);
+
+ /* Optimize cbrt of constant value. */
+ if (real_zerop (arg) || real_onep (arg) || real_minus_onep (arg))
+ return arg;
+
+ /* Optimize cbrt(expN(x)) -> expN(x/3). */
+ if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode))
+ {
+ tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
+ const REAL_VALUE_TYPE third_trunc =
+ real_value_truncate (TYPE_MODE (type), dconstthird);
+ arg = fold (build (MULT_EXPR, type,
+ TREE_VALUE (TREE_OPERAND (arg, 1)),
+ build_real (type, third_trunc)));
+ arglist = build_tree_list (NULL_TREE, arg);
+ return build_function_call_expr (expfn, arglist);
+ }
+
+ /* Optimize cbrt(sqrt(x)) -> pow(x,1/6). */
+ /* We don't optimize cbrt(cbrt(x)) -> pow(x,1/9) because if
+ x is negative pow will error but cbrt won't. */
+ if (flag_unsafe_math_optimizations && BUILTIN_SQRT_P (fcode))
+ {
+ tree powfn = mathfn_built_in (type, BUILT_IN_POW);
+
+ if (powfn)
+ {
+ tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
+ tree tree_root;
+ REAL_VALUE_TYPE dconstroot = dconstthird;
+
+ SET_REAL_EXP (&dconstroot, REAL_EXP (&dconstroot) - 1);
+ dconstroot = real_value_truncate (TYPE_MODE (type), dconstroot);
+ tree_root = build_real (type, dconstroot);
+ arglist = tree_cons (NULL_TREE, arg0,
+ build_tree_list (NULL_TREE, tree_root));
+ return build_function_call_expr (powfn, arglist);
+ }
+
+ }
+ }
+ break;
+
case BUILT_IN_SIN:
case BUILT_IN_SINF:
case BUILT_IN_SINL:
/* Optimize sin(0.0) = 0.0. */
if (real_zerop (arg))
- return build_real (type, dconst0);
+ return arg;
}
break;
/* Optimize cos(0.0) = 1.0. */
if (real_zerop (arg))
return build_real (type, dconst1);
+
+ /* Optimize cos(-x) into cos(x). */
+ if (TREE_CODE (arg) == NEGATE_EXPR)
+ {
+ tree arglist = build_tree_list (NULL_TREE,
+ TREE_OPERAND (arg, 0));
+ return build_function_call_expr (fndecl, arglist);
+ }
}
break;
case BUILT_IN_EXP:
case BUILT_IN_EXPF:
case BUILT_IN_EXPL:
+ return fold_builtin_exponent (exp, &dconste);
+ case BUILT_IN_EXP2:
+ case BUILT_IN_EXP2F:
+ case BUILT_IN_EXP2L:
+ return fold_builtin_exponent (exp, &dconst2);
+ case BUILT_IN_EXP10:
+ case BUILT_IN_EXP10F:
+ case BUILT_IN_EXP10L:
+ case BUILT_IN_POW10:
+ case BUILT_IN_POW10F:
+ case BUILT_IN_POW10L:
+ return fold_builtin_exponent (exp, &dconst10);
+ case BUILT_IN_LOG:
+ case BUILT_IN_LOGF:
+ case BUILT_IN_LOGL:
+ return fold_builtin_logarithm (exp, &dconste);
+ break;
+ case BUILT_IN_LOG2:
+ case BUILT_IN_LOG2F:
+ case BUILT_IN_LOG2L:
+ return fold_builtin_logarithm (exp, &dconst2);
+ break;
+ case BUILT_IN_LOG10:
+ case BUILT_IN_LOG10F:
+ case BUILT_IN_LOG10L:
+ return fold_builtin_logarithm (exp, &dconst10);
+ break;
+
+ case BUILT_IN_TAN:
+ case BUILT_IN_TANF:
+ case BUILT_IN_TANL:
if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
{
enum built_in_function fcode;
tree arg = TREE_VALUE (arglist);
- /* Optimize exp(0.0) = 1.0. */
+ /* Optimize tan(0.0) = 0.0. */
if (real_zerop (arg))
- return build_real (type, dconst1);
+ return arg;
- /* Optimize exp(log(x)) = x. */
+ /* Optimize tan(atan(x)) = x. */
fcode = builtin_mathfn_code (arg);
if (flag_unsafe_math_optimizations
- && (fcode == BUILT_IN_LOG
- || fcode == BUILT_IN_LOGF
- || fcode == BUILT_IN_LOGL))
+ && (fcode == BUILT_IN_ATAN
+ || fcode == BUILT_IN_ATANF
+ || fcode == BUILT_IN_ATANL))
return TREE_VALUE (TREE_OPERAND (arg, 1));
}
break;
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
+ case BUILT_IN_ATAN:
+ case BUILT_IN_ATANF:
+ case BUILT_IN_ATANL:
if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
{
- enum built_in_function fcode;
tree arg = TREE_VALUE (arglist);
- /* Optimize log(1.0) = 0.0. */
- if (real_onep (arg))
- return build_real (type, dconst0);
-
- /* Optimize log(exp(x)) = x. */
- fcode = builtin_mathfn_code (arg);
- if (flag_unsafe_math_optimizations
- && (fcode == BUILT_IN_EXP
- || fcode == BUILT_IN_EXPF
- || fcode == BUILT_IN_EXPL))
- return TREE_VALUE (TREE_OPERAND (arg, 1));
-
- /* Optimize log(sqrt(x)) = log(x)*0.5. */
- if (flag_unsafe_math_optimizations
- && (fcode == BUILT_IN_SQRT
- || fcode == BUILT_IN_SQRTF
- || fcode == BUILT_IN_SQRTL))
- {
- tree logfn = build_function_call_expr (fndecl,
- TREE_OPERAND (arg, 1));
- return fold (build (MULT_EXPR, type, logfn,
- build_real (type, dconsthalf)));
- }
+ /* Optimize atan(0.0) = 0.0. */
+ if (real_zerop (arg))
+ return arg;
- /* Optimize log(pow(x,y)) = y*log(x). */
- if (flag_unsafe_math_optimizations
- && (fcode == BUILT_IN_POW
- || fcode == BUILT_IN_POWF
- || fcode == BUILT_IN_POWL))
+ /* Optimize atan(1.0) = pi/4. */
+ if (real_onep (arg))
{
- tree arg0, arg1, logfn;
+ REAL_VALUE_TYPE cst;
- arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
- arg1 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1)));
- arglist = build_tree_list (NULL_TREE, arg0);
- logfn = build_function_call_expr (fndecl, arglist);
- return fold (build (MULT_EXPR, type, arg1, logfn));
+ real_convert (&cst, TYPE_MODE (type), &dconstpi);
+ SET_REAL_EXP (&cst, REAL_EXP (&cst) - 2);
+ return build_real (type, cst);
}
}
break;
build_real (type, dconst1),
arg0));
- /* Optimize pow(x,2.0) = x*x. */
- if (REAL_VALUES_EQUAL (c, dconst2)
- && (*lang_hooks.decls.global_bindings_p) () == 0
- && ! contains_placeholder_p (arg0))
- {
- arg0 = save_expr (arg0);
- return fold (build (MULT_EXPR, type, arg0, arg0));
- }
-
- /* Optimize pow(x,-2.0) = 1.0/(x*x). */
- if (flag_unsafe_math_optimizations
- && REAL_VALUES_EQUAL (c, dconstm2)
- && (*lang_hooks.decls.global_bindings_p) () == 0
- && ! contains_placeholder_p (arg0))
- {
- arg0 = save_expr (arg0);
- return fold (build (RDIV_EXPR, type,
- build_real (type, dconst1),
- fold (build (MULT_EXPR, type,
- arg0, arg0))));
- }
-
/* Optimize pow(x,0.5) = sqrt(x). */
if (flag_unsafe_math_optimizations
&& REAL_VALUES_EQUAL (c, dconsthalf))
REAL_VALUE_TYPE cint;
HOST_WIDE_INT n;
- n = real_to_integer(&c);
+ n = real_to_integer (&c);
real_from_integer (&cint, VOIDmode, n,
n < 0 ? -1 : 0, 0);
if (real_identical (&c, &cint))
}
}
- /* Optimize pow(exp(x),y) = exp(x*y). */
+ /* Optimize pow(expN(x),y) = expN(x*y). */
fcode = builtin_mathfn_code (arg0);
- if (flag_unsafe_math_optimizations
- && (fcode == BUILT_IN_EXP
- || fcode == BUILT_IN_EXPF
- || fcode == BUILT_IN_EXPL))
+ if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode))
{
tree expfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0);
tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1));
}
/* Optimize pow(sqrt(x),y) = pow(x,y*0.5). */
- if (flag_unsafe_math_optimizations
- && (fcode == BUILT_IN_SQRT
- || fcode == BUILT_IN_SQRTF
- || fcode == BUILT_IN_SQRTL))
+ if (flag_unsafe_math_optimizations && BUILTIN_SQRT_P (fcode))
{
tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1));
tree narg1 = fold (build (MULT_EXPR, type, arg1,
case BUILT_IN_FLOOR:
case BUILT_IN_FLOORF:
case BUILT_IN_FLOORL:
+ return fold_builtin_floor (exp);
+
case BUILT_IN_CEIL:
case BUILT_IN_CEILF:
case BUILT_IN_CEILL:
+ return fold_builtin_ceil (exp);
+
case BUILT_IN_TRUNC:
case BUILT_IN_TRUNCF:
case BUILT_IN_TRUNCL:
+ return fold_builtin_trunc (exp);
+
case BUILT_IN_ROUND:
case BUILT_IN_ROUNDF:
case BUILT_IN_ROUNDL:
+ return fold_builtin_round (exp);
+
case BUILT_IN_NEARBYINT:
case BUILT_IN_NEARBYINTF:
case BUILT_IN_NEARBYINTL:
+ case BUILT_IN_RINT:
+ case BUILT_IN_RINTF:
+ case BUILT_IN_RINTL:
return fold_trunc_transparent_mathfn (exp);
+ case BUILT_IN_FFS:
+ case BUILT_IN_FFSL:
+ case BUILT_IN_FFSLL:
+ case BUILT_IN_CLZ:
+ case BUILT_IN_CLZL:
+ case BUILT_IN_CLZLL:
+ case BUILT_IN_CTZ:
+ case BUILT_IN_CTZL:
+ case BUILT_IN_CTZLL:
+ case BUILT_IN_POPCOUNT:
+ case BUILT_IN_POPCOUNTL:
+ case BUILT_IN_POPCOUNTLL:
+ case BUILT_IN_PARITY:
+ case BUILT_IN_PARITYL:
+ case BUILT_IN_PARITYLL:
+ return fold_builtin_bitop (exp);
+
+ case BUILT_IN_MEMCPY:
+ return fold_builtin_memcpy (exp);
+
+ case BUILT_IN_MEMPCPY:
+ return fold_builtin_mempcpy (exp);
+
+ case BUILT_IN_MEMMOVE:
+ return fold_builtin_memmove (exp);
+
+ case BUILT_IN_STRCPY:
+ return fold_builtin_strcpy (exp);
+
+ case BUILT_IN_STRNCPY:
+ return fold_builtin_strncpy (exp);
+
+ case BUILT_IN_MEMCMP:
+ return fold_builtin_memcmp (exp);
+
+ case BUILT_IN_STRCMP:
+ return fold_builtin_strcmp (exp);
+
+ case BUILT_IN_STRNCMP:
+ return fold_builtin_strncmp (exp);
+
+ case BUILT_IN_SIGNBIT:
+ case BUILT_IN_SIGNBITF:
+ case BUILT_IN_SIGNBITL:
+ return fold_builtin_signbit (exp);
+
default:
break;
}
/* Conveniently construct a function call expression. */
tree
-build_function_call_expr (fn, arglist)
- tree fn, arglist;
+build_function_call_expr (tree fn, tree arglist)
{
tree call_expr;
call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
call_expr, arglist);
- TREE_SIDE_EFFECTS (call_expr) = 1;
return fold (call_expr);
}
enum tree_code code;
int res = 0;
va_list ap;
-
+
va_start (ap, arglist);
do
return res;
}
-/* Default version of target-specific builtin setup that does nothing. */
-
-void
-default_init_builtins ()
-{
-}
-
/* Default target-specific builtin expander that does nothing. */
rtx
-default_expand_builtin (exp, target, subtarget, mode, ignore)
- tree exp ATTRIBUTE_UNUSED;
- rtx target ATTRIBUTE_UNUSED;
- rtx subtarget ATTRIBUTE_UNUSED;
- enum machine_mode mode ATTRIBUTE_UNUSED;
- int ignore ATTRIBUTE_UNUSED;
+default_expand_builtin (tree exp ATTRIBUTE_UNUSED,
+ rtx target ATTRIBUTE_UNUSED,
+ rtx subtarget ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ int ignore ATTRIBUTE_UNUSED)
{
return NULL_RTX;
}
/* Instantiate all remaining CONSTANT_P_RTX nodes. */
void
-purge_builtin_constant_p ()
+purge_builtin_constant_p (void)
{
rtx insn, set, arg, new, note;