/* Expand builtin functions.
Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
This file is part of GCC.
#include "basic-block.h"
#include "tree-mudflap.h"
-#define CALLED_AS_BUILT_IN(NODE) \
- (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
-
#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) #X,
-const char *const built_in_names[(int) END_BUILTINS] =
+#define DEF_BUILTIN(X, N, C, T, LT, B, F, NA, AT, IM, COND) #X,
+const char * built_in_names[(int) END_BUILTINS] =
{
#include "builtins.def"
};
static rtx expand_builtin_mathfn (tree, rtx, rtx);
static rtx expand_builtin_mathfn_2 (tree, rtx, rtx);
static rtx expand_builtin_mathfn_3 (tree, rtx, rtx);
+static rtx expand_builtin_int_roundingfn (tree, rtx, rtx);
static rtx expand_builtin_args_info (tree);
-static rtx expand_builtin_next_arg (tree);
+static rtx expand_builtin_next_arg (void);
static rtx expand_builtin_va_start (tree);
static rtx expand_builtin_va_end (tree);
static rtx expand_builtin_va_copy (tree);
static rtx expand_builtin_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_strcat (tree, 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_mempcpy (tree, tree, rtx, enum machine_mode, int);
+static rtx expand_builtin_memmove (tree, tree, rtx, enum machine_mode, tree);
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 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_memset (tree, rtx, enum machine_mode, tree);
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_strstr (tree, tree, rtx, enum machine_mode);
+static rtx expand_builtin_strpbrk (tree, tree, rtx, enum machine_mode);
+static rtx expand_builtin_strchr (tree, tree, rtx, enum machine_mode);
+static rtx expand_builtin_strrchr (tree, 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 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 tree fold_trunc_transparent_mathfn (tree, tree);
static bool readonly_data_expr (tree);
static rtx expand_builtin_fabs (tree, rtx, rtx);
static rtx expand_builtin_signbit (tree, rtx);
static tree fold_builtin_sqrt (tree, tree);
static tree fold_builtin_cbrt (tree, tree);
static tree fold_builtin_pow (tree, tree, tree);
+static tree fold_builtin_powi (tree, tree, tree);
static tree fold_builtin_sin (tree);
static tree fold_builtin_cos (tree, tree, tree);
static tree fold_builtin_tan (tree);
static tree fold_builtin_atan (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_strchr (tree, bool);
+static tree fold_builtin_trunc (tree, tree);
+static tree fold_builtin_floor (tree, tree);
+static tree fold_builtin_ceil (tree, tree);
+static tree fold_builtin_round (tree, tree);
+static tree fold_builtin_int_roundingfn (tree, tree);
+static tree fold_builtin_bitop (tree, tree);
+static tree fold_builtin_memcpy (tree, tree);
+static tree fold_builtin_mempcpy (tree, tree, int);
+static tree fold_builtin_memmove (tree, tree);
+static tree fold_builtin_strchr (tree, 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);
-static tree fold_builtin_copysign (tree, tree);
+static tree fold_builtin_signbit (tree, tree);
+static tree fold_builtin_copysign (tree, tree, tree);
static tree fold_builtin_isascii (tree);
static tree fold_builtin_toascii (tree);
static tree fold_builtin_isdigit (tree);
static tree fold_builtin_fabs (tree, tree);
static tree fold_builtin_abs (tree, tree);
-static tree fold_builtin_unordered_cmp (tree, enum tree_code, enum tree_code);
-static tree fold_builtin_1 (tree, bool);
-
-static tree simplify_builtin_strpbrk (tree);
-static tree simplify_builtin_strstr (tree);
-static tree simplify_builtin_strchr (tree);
-static tree simplify_builtin_strrchr (tree);
-static tree simplify_builtin_strcat (tree);
-static tree simplify_builtin_strncat (tree);
-static tree simplify_builtin_strspn (tree);
-static tree simplify_builtin_strcspn (tree);
-static void simplify_builtin_next_arg (tree);
-static void simplify_builtin_va_start (tree);
-static tree simplify_builtin_sprintf (tree, int);
-
+static tree fold_builtin_unordered_cmp (tree, tree, enum tree_code,
+ enum tree_code);
+static tree fold_builtin_1 (tree, tree, bool);
+
+static tree fold_builtin_strpbrk (tree, tree);
+static tree fold_builtin_strstr (tree, tree);
+static tree fold_builtin_strrchr (tree, tree);
+static tree fold_builtin_strcat (tree);
+static tree fold_builtin_strncat (tree);
+static tree fold_builtin_strspn (tree);
+static tree fold_builtin_strcspn (tree);
+static tree fold_builtin_sprintf (tree, int);
+
+/* Return true if NODE should be considered for inline expansion regardless
+ of the optimization level. This means whenever a function is invoked with
+ its "internal" name, which normally contains the prefix "__builtin". */
+
+static bool called_as_built_in (tree node)
+{
+ const char *name = IDENTIFIER_POINTER (DECL_NAME (node));
+ if (strncmp (name, "__builtin_", 10) == 0)
+ return true;
+ if (strncmp (name, "__sync_", 7) == 0)
+ return true;
+ return false;
+}
/* Return the alignment in bits of EXP, a pointer valued expression.
But don't return more than MAX_ALIGN no matter what.
{
unsigned int align, inner;
- if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE)
+ if (! POINTER_TYPE_P (TREE_TYPE (exp)))
return 0;
align = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp)));
case CONVERT_EXPR:
case NON_LVALUE_EXPR:
exp = TREE_OPERAND (exp, 0);
- if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE)
+ if (! POINTER_TYPE_P (TREE_TYPE (exp)))
return align;
inner = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp)));
else if (DECL_P (exp))
align = DECL_ALIGN (exp);
#ifdef CONSTANT_ALIGNMENT
- else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c')
+ else if (CONSTANT_CLASS_P (exp))
align = CONSTANT_ALIGNMENT (exp, align);
#endif
return MIN (align, max_align);
runtime. */
if (offset < 0 || offset > max)
{
- warning ("offset outside bounds of constant string");
+ warning (0, "offset outside bounds of constant string");
return 0;
}
times to get the address of either a higher stack frame, or a return
address located within it (depending on FNDECL_CODE). */
-rtx
-expand_builtin_return_addr (enum built_in_function fndecl_code, int count,
- rtx tem)
+static rtx
+expand_builtin_return_addr (enum built_in_function fndecl_code, int count)
{
int i;
+#ifdef INITIAL_FRAME_ADDRESS_RTX
+ rtx tem = INITIAL_FRAME_ADDRESS_RTX;
+#else
+ rtx tem = hard_frame_pointer_rtx;
+#endif
+
/* Some machines need special handling before we can access
arbitrary frames. For example, on the sparc, we must first flush
all register windows to the stack. */
scheme in the compiler and will only work in the method used by
them. */
-void
+static void
expand_builtin_longjmp (rtx buf_addr, rtx value)
{
rtx fp, lab, stack, insn, last;
longer copying around a value that we don't care about. */
gcc_assert (value == const1_rtx);
- current_function_calls_longjmp = 1;
-
last = get_last_insn ();
#ifdef HAVE_builtin_longjmp
if (HAVE_builtin_longjmp)
/* Argument 1 (read/write flag) must be a compile-time constant int. */
if (TREE_CODE (arg1) != INTEGER_CST)
{
- error ("second arg to `__builtin_prefetch' must be a constant");
+ error ("second argument to %<__builtin_prefetch%> must be a constant");
arg1 = integer_zero_node;
}
op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
/* Argument 1 must be either zero or one. */
if (INTVAL (op1) != 0 && INTVAL (op1) != 1)
{
- warning ("invalid second arg to __builtin_prefetch; using zero");
+ warning (0, "invalid second argument to %<__builtin_prefetch%>;"
+ " using zero");
op1 = const0_rtx;
}
/* Argument 2 (locality) must be a compile-time constant int. */
if (TREE_CODE (arg2) != INTEGER_CST)
{
- error ("third arg to `__builtin_prefetch' must be a constant");
+ error ("third argument to %<__builtin_prefetch%> must be a constant");
arg2 = integer_zero_node;
}
op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0);
/* Argument 2 must be 0, 1, 2, or 3. */
if (INTVAL (op2) < 0 || INTVAL (op2) > 3)
{
- warning ("invalid third arg to __builtin_prefetch; using zero");
+ warning (0, "invalid third argument to %<__builtin_prefetch%>; using zero");
op2 = const0_rtx;
}
static rtx
get_memory_rtx (tree exp)
{
- rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_SUM);
- rtx mem;
-
- addr = convert_memory_address (Pmode, addr);
-
- mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
+ rtx addr = expand_expr (exp, NULL_RTX, ptr_mode, EXPAND_NORMAL);
+ rtx mem = gen_rtx_MEM (BLKmode, memory_address (BLKmode, addr));
/* Get an expression we can use to find the attributes to assign to MEM.
If it is an ADDR_EXPR, use the operand. Otherwise, dereference it if
exp = TREE_OPERAND (exp, 0);
if (TREE_CODE (exp) == ADDR_EXPR)
- {
- exp = TREE_OPERAND (exp, 0);
- set_mem_attributes (mem, exp, 0);
- }
+ exp = TREE_OPERAND (exp, 0);
else if (POINTER_TYPE_P (TREE_TYPE (exp)))
+ exp = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (exp)), exp);
+ else
+ exp = NULL;
+
+ /* Honor attributes derived from exp, except for the alias set
+ (as builtin stringops may alias with anything) and the size
+ (as stringops may access multiple array elements). */
+ if (exp)
{
- exp = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (exp)), exp);
- /* memcpy, memset and other builtin stringops can alias with anything. */
+ set_mem_attributes (mem, exp, 0);
set_mem_alias_set (mem, 0);
+ set_mem_size (mem, NULL_RTX);
}
return mem;
case QUAL_UNION_TYPE: return union_type_class;
case ARRAY_TYPE: return (TYPE_STRING_FLAG (type)
? string_type_class : array_type_class);
- case SET_TYPE: return set_type_class;
- case FILE_TYPE: return file_type_class;
case LANG_TYPE: return lang_type_class;
default: return no_type_class;
}
CASE_MATHFN (BUILT_IN_J0)
CASE_MATHFN (BUILT_IN_J1)
CASE_MATHFN (BUILT_IN_JN)
+ CASE_MATHFN (BUILT_IN_LCEIL)
CASE_MATHFN (BUILT_IN_LDEXP)
+ CASE_MATHFN (BUILT_IN_LFLOOR)
CASE_MATHFN (BUILT_IN_LGAMMA)
+ CASE_MATHFN (BUILT_IN_LLCEIL)
+ CASE_MATHFN (BUILT_IN_LLFLOOR)
CASE_MATHFN (BUILT_IN_LLRINT)
CASE_MATHFN (BUILT_IN_LLROUND)
CASE_MATHFN (BUILT_IN_LOG)
CASE_MATHFN (BUILT_IN_NEXTAFTER)
CASE_MATHFN (BUILT_IN_NEXTTOWARD)
CASE_MATHFN (BUILT_IN_POW)
+ CASE_MATHFN (BUILT_IN_POWI)
CASE_MATHFN (BUILT_IN_POW10)
CASE_MATHFN (BUILT_IN_REMAINDER)
CASE_MATHFN (BUILT_IN_REMQUO)
case BUILT_IN_RINTF:
case BUILT_IN_RINTL:
builtin_optab = rint_optab; break;
+ case BUILT_IN_LRINT:
+ case BUILT_IN_LRINTF:
+ case BUILT_IN_LRINTL:
+ case BUILT_IN_LLRINT:
+ case BUILT_IN_LLRINTF:
+ case BUILT_IN_LLRINTL:
+ builtin_optab = lrint_optab; break;
default:
gcc_unreachable ();
}
narg = builtin_save_expr (arg);
if (narg != arg)
{
+ arg = narg;
arglist = build_tree_list (NULL_TREE, arg);
exp = build_function_call_expr (fndecl, arglist);
}
{
optab builtin_optab;
rtx op0, op1, insns;
+ int op1_type = REAL_TYPE;
tree fndecl = get_callee_fndecl (exp);
tree arglist = TREE_OPERAND (exp, 1);
tree arg0, arg1, temp, narg;
bool errno_set = true;
bool stable = true;
- if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+ if ((DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXP)
+ || (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXPF)
+ || (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_LDEXPL))
+ op1_type = INTEGER_TYPE;
+
+ if (!validate_arglist (arglist, REAL_TYPE, op1_type, VOID_TYPE))
return 0;
arg0 = TREE_VALUE (arglist);
case BUILT_IN_ATAN2F:
case BUILT_IN_ATAN2L:
builtin_optab = atan2_optab; break;
+ case BUILT_IN_LDEXP:
+ case BUILT_IN_LDEXPF:
+ case BUILT_IN_LDEXPL:
+ builtin_optab = ldexp_optab; break;
case BUILT_IN_FMOD:
case BUILT_IN_FMODF:
case BUILT_IN_FMODL:
narg = builtin_save_expr (arg1);
if (narg != arg1)
{
+ arg1 = narg;
temp = build_tree_list (NULL_TREE, narg);
stable = false;
}
narg = builtin_save_expr (arg0);
if (narg != arg0)
{
+ arg0 = narg;
arglist = tree_cons (NULL_TREE, narg, temp);
stable = false;
}
expand_builtin_mathfn_3 (tree exp, rtx target, rtx subtarget)
{
optab builtin_optab;
- rtx op0, insns, before_call;
+ rtx op0, insns;
tree fndecl = get_callee_fndecl (exp);
tree arglist = TREE_OPERAND (exp, 1);
enum machine_mode mode;
narg = save_expr (arg);
if (narg != arg)
{
+ arg = narg;
arglist = build_tree_list (NULL_TREE, arg);
exp = build_function_call_expr (fndecl, arglist);
}
end_sequence ();
}
- before_call = get_last_insn ();
-
target = expand_call (exp, target, target == const0_rtx);
return target;
}
+/* Expand a call to one of the builtin rounding functions (lfloor).
+ If expanding via optab fails, lower expression to (int)(floor(x)).
+ EXP is the expression that is a call to the builtin function;
+ if convenient, the result should be placed in TARGET. SUBTARGET may
+ be used as the target for computing one of EXP's operands. */
+
+static rtx
+expand_builtin_int_roundingfn (tree exp, rtx target, rtx subtarget)
+{
+ optab builtin_optab;
+ rtx op0, insns, tmp;
+ tree fndecl = get_callee_fndecl (exp);
+ tree arglist = TREE_OPERAND (exp, 1);
+ enum built_in_function fallback_fn;
+ tree fallback_fndecl;
+ enum machine_mode mode;
+ tree arg, narg;
+
+ if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ gcc_unreachable ();
+
+ arg = TREE_VALUE (arglist);
+
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_LCEIL:
+ case BUILT_IN_LCEILF:
+ case BUILT_IN_LCEILL:
+ case BUILT_IN_LLCEIL:
+ case BUILT_IN_LLCEILF:
+ case BUILT_IN_LLCEILL:
+ builtin_optab = lceil_optab;
+ fallback_fn = BUILT_IN_CEIL;
+ break;
+
+ case BUILT_IN_LFLOOR:
+ case BUILT_IN_LFLOORF:
+ case BUILT_IN_LFLOORL:
+ case BUILT_IN_LLFLOOR:
+ case BUILT_IN_LLFLOORF:
+ case BUILT_IN_LLFLOORL:
+ builtin_optab = lfloor_optab;
+ fallback_fn = BUILT_IN_FLOOR;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ /* 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)
+ {
+ target = gen_reg_rtx (mode);
+
+ /* Wrap the computation of the argument in a SAVE_EXPR, as we may
+ need to expand the argument again. This way, we will not perform
+ side-effects more the once. */
+ narg = builtin_save_expr (arg);
+ if (narg != arg)
+ {
+ arg = narg;
+ arglist = build_tree_list (NULL_TREE, arg);
+ exp = build_function_call_expr (fndecl, arglist);
+ }
+
+ op0 = expand_expr (arg, subtarget, VOIDmode, 0);
+
+ 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)
+ {
+ /* 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). */
+ end_sequence ();
+ }
+
+ /* Fall back to floating point rounding optab. */
+ fallback_fndecl = mathfn_built_in (TREE_TYPE (arg), fallback_fn);
+ /* We shouldn't get here on targets without TARGET_C99_FUNCTIONS.
+ ??? Perhaps convert (int)floorf(x) into (int)floor((double)x). */
+ gcc_assert (fallback_fndecl != NULL_TREE);
+ exp = build_function_call_expr (fallback_fndecl, arglist);
+
+ tmp = expand_builtin_mathfn (exp, NULL_RTX, NULL_RTX);
+
+ /* Truncate the result of floating point optab to integer
+ via expand_fix (). */
+ target = gen_reg_rtx (mode);
+ expand_fix (target, tmp, 0);
+
+ return target;
+}
+
/* 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
return expand_builtin_mathfn_2 (exp, target, subtarget);
}
+/* Expand a call to the powi 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_powi (tree exp, rtx target, rtx subtarget)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg0, arg1;
+ rtx op0, op1;
+ enum machine_mode mode;
+ enum machine_mode mode2;
+
+ if (! validate_arglist (arglist, REAL_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ arg0 = TREE_VALUE (arglist);
+ arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+ mode = TYPE_MODE (TREE_TYPE (exp));
+
+ /* Handle constant power. */
+
+ if (TREE_CODE (arg1) == INTEGER_CST
+ && ! TREE_CONSTANT_OVERFLOW (arg1))
+ {
+ HOST_WIDE_INT n = TREE_INT_CST_LOW (arg1);
+
+ /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact.
+ Otherwise, check the number of multiplications required. */
+ if ((TREE_INT_CST_HIGH (arg1) == 0
+ || TREE_INT_CST_HIGH (arg1) == -1)
+ && ((n >= -1 && n <= 2)
+ || (! optimize_size
+ && powi_cost (n) <= POWI_MAX_MULTS)))
+ {
+ op0 = expand_expr (arg0, subtarget, VOIDmode, 0);
+ op0 = force_reg (mode, op0);
+ return expand_powi (op0, mode, n);
+ }
+ }
+
+ /* Emit a libcall to libgcc. */
+
+ /* Mode of the 2nd argument must match that of an int. */
+ mode2 = mode_for_size (INT_TYPE_SIZE, MODE_INT, 0);
+
+ if (target == NULL_RTX)
+ target = gen_reg_rtx (mode);
+
+ op0 = expand_expr (arg0, subtarget, mode, 0);
+ if (GET_MODE (op0) != mode)
+ op0 = convert_to_mode (mode, op0, 0);
+ op1 = expand_expr (arg1, 0, mode2, 0);
+ if (GET_MODE (op1) != mode2)
+ op1 = convert_to_mode (mode2, op1, 0);
+
+ target = emit_library_call_value (powi_optab->handlers[(int) mode].libfunc,
+ target, LCT_CONST_MAKE_BLOCK, mode, 2,
+ op0, mode, op1, mode2);
+
+ 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. */
/* Now that we are assured of success, expand the source. */
start_sequence ();
- pat = memory_address (BLKmode,
- expand_expr (src, src_reg, ptr_mode, EXPAND_SUM));
+ pat = expand_expr (src, src_reg, ptr_mode, EXPAND_NORMAL);
if (pat != src_reg)
emit_move_insn (src_reg, pat);
pat = get_insns ();
in TARGET, if convenient (and in mode MODE if that's convenient). */
static rtx
-expand_builtin_strstr (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_strstr (tree arglist, tree type, rtx target, enum machine_mode mode)
{
- if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
- return 0;
- else
+ if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
{
- tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
- tree fn, tmp;
- const char *p1, *p2;
-
- p2 = c_getstr (s2);
- if (p2 == NULL)
- return 0;
-
- p1 = c_getstr (s1);
- if (p1 != NULL)
- {
- const char *r = strstr (p1, p2);
-
- if (r == NULL)
- return const0_rtx;
-
- /* Return an offset into the constant string argument. */
- tmp = fold (build2 (PLUS_EXPR, TREE_TYPE (s1), s1,
- fold_convert (TREE_TYPE (s1),
- ssize_int (r - p1))));
- return expand_expr (tmp, target, mode, EXPAND_NORMAL);
- }
-
- if (p2[0] == '\0')
- return expand_expr (s1, target, mode, EXPAND_NORMAL);
-
- if (p2[1] != '\0')
- return 0;
-
- fn = implicit_built_in_decls[BUILT_IN_STRCHR];
- if (!fn)
- return 0;
-
- /* New argument list transforming strstr(s1, s2) to
- strchr(s1, s2[0]). */
- arglist = build_tree_list (NULL_TREE,
- build_int_cst (NULL_TREE, p2[0]));
- arglist = tree_cons (NULL_TREE, s1, arglist);
- return expand_expr (build_function_call_expr (fn, arglist),
- target, mode, EXPAND_NORMAL);
+ tree result = fold_builtin_strstr (arglist, type);
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
}
+ return 0;
}
/* Expand a call to the strchr builtin. Return 0 if we failed the
in TARGET, if convenient (and in mode MODE if that's convenient). */
static rtx
-expand_builtin_strchr (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_strchr (tree arglist, tree type, rtx target, enum machine_mode mode)
{
- if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
- return 0;
- else
+ if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
{
- tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
- const char *p1;
-
- if (TREE_CODE (s2) != INTEGER_CST)
- return 0;
-
- p1 = c_getstr (s1);
- if (p1 != NULL)
- {
- char c;
- const char *r;
- tree tmp;
-
- if (target_char_cast (s2, &c))
- return 0;
-
- r = strchr (p1, c);
-
- if (r == NULL)
- return const0_rtx;
-
- /* Return an offset into the constant string argument. */
- tmp = fold (build2 (PLUS_EXPR, TREE_TYPE (s1), s1,
- fold_convert (TREE_TYPE (s1),
- ssize_int (r - p1))));
- return expand_expr (tmp, target, mode, EXPAND_NORMAL);
- }
+ tree result = fold_builtin_strchr (arglist, type);
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
- /* FIXME: Should use here strchrM optab so that ports can optimize
- this. */
- return 0;
+ /* FIXME: Should use strchrM optab so that ports can optimize this. */
}
+ return 0;
}
/* Expand a call to the strrchr builtin. Return 0 if we failed the
in TARGET, if convenient (and in mode MODE if that's convenient). */
static rtx
-expand_builtin_strrchr (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_strrchr (tree arglist, tree type, rtx target, enum machine_mode mode)
{
- if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
- return 0;
- else
+ if (validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
{
- tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
- tree fn, tmp;
- const char *p1;
-
- if (TREE_CODE (s2) != INTEGER_CST)
- return 0;
-
- p1 = c_getstr (s1);
- if (p1 != NULL)
- {
- char c;
- const char *r;
-
- if (target_char_cast (s2, &c))
- return 0;
-
- r = strrchr (p1, c);
-
- if (r == NULL)
- return const0_rtx;
-
- /* Return an offset into the constant string argument. */
- tmp = fold (build2 (PLUS_EXPR, TREE_TYPE (s1), s1,
- fold_convert (TREE_TYPE (s1),
- ssize_int (r - p1))));
- return expand_expr (tmp, target, mode, EXPAND_NORMAL);
- }
-
- if (! integer_zerop (s2))
- return 0;
-
- fn = implicit_built_in_decls[BUILT_IN_STRCHR];
- if (!fn)
- return 0;
-
- /* Transform strrchr(s1, '\0') to strchr(s1, '\0'). */
- return expand_expr (build_function_call_expr (fn, arglist),
- target, mode, EXPAND_NORMAL);
+ tree result = fold_builtin_strrchr (arglist, type);
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
}
+ return 0;
}
/* Expand a call to the strpbrk builtin. Return 0 if we failed the
in TARGET, if convenient (and in mode MODE if that's convenient). */
static rtx
-expand_builtin_strpbrk (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_strpbrk (tree arglist, tree type, rtx target, enum machine_mode mode)
{
- if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
- return 0;
- else
+ if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
{
- tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
- tree fn, tmp;
- const char *p1, *p2;
-
- p2 = c_getstr (s2);
- if (p2 == NULL)
- return 0;
+ tree result = fold_builtin_strpbrk (arglist, type);
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
+ }
+ return 0;
+}
- p1 = c_getstr (s1);
- if (p1 != NULL)
- {
- const char *r = strpbrk (p1, p2);
-
- if (r == NULL)
- return const0_rtx;
-
- /* Return an offset into the constant string argument. */
- tmp = fold (build2 (PLUS_EXPR, TREE_TYPE (s1), s1,
- fold_convert (TREE_TYPE (s1),
- ssize_int (r - p1))));
- return expand_expr (tmp, target, mode, EXPAND_NORMAL);
- }
-
- if (p2[0] == '\0')
- {
- /* strpbrk(x, "") == NULL.
- Evaluate and ignore the arguments in case they had
- side-effects. */
- expand_expr (s1, const0_rtx, VOIDmode, EXPAND_NORMAL);
- return const0_rtx;
- }
-
- if (p2[1] != '\0')
- return 0; /* Really call strpbrk. */
-
- fn = implicit_built_in_decls[BUILT_IN_STRCHR];
- if (!fn)
- return 0;
-
- /* New argument list transforming strpbrk(s1, s2) to
- strchr(s1, s2[0]). */
- arglist = build_tree_list (NULL_TREE,
- build_int_cst (NULL_TREE, p2[0]));
- arglist = tree_cons (NULL_TREE, s1, arglist);
- return expand_expr (build_function_call_expr (fn, arglist),
- target, mode, EXPAND_NORMAL);
- }
-}
-
-/* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE)
- bytes from constant string DATA + OFFSET and return it as target
- constant. */
+/* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE)
+ bytes from constant string DATA + OFFSET and return it as target
+ constant. */
static rtx
builtin_memcpy_read_str (void *data, HOST_WIDE_INT offset,
otherwise try to get the result in TARGET, if convenient (and in
mode MODE if that's convenient). */
static rtx
-expand_builtin_memcpy (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_memcpy (tree exp, rtx target, enum machine_mode mode)
{
+ tree fndecl = get_callee_fndecl (exp);
+ tree arglist = TREE_OPERAND (exp, 1);
if (!validate_arglist (arglist,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
unsigned int dest_align
= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
rtx dest_mem, src_mem, dest_addr, len_rtx;
+ tree result = fold_builtin_memcpy (fndecl, arglist);
+
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
/* If DEST is not a pointer type, call the normal function. */
if (dest_align == 0)
return 0;
- /* If the LEN parameter is zero, return DEST. */
- 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)
/* Copy word part most expediently. */
dest_addr = emit_block_move (dest_mem, src_mem, len_rtx,
- BLOCK_OP_NORMAL);
+ CALL_EXPR_TAILCALL (exp)
+ ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL);
if (dest_addr == 0)
{
stpcpy. */
static rtx
-expand_builtin_mempcpy (tree arglist, rtx target, enum machine_mode mode,
+expand_builtin_mempcpy (tree arglist, tree type, rtx target, enum machine_mode mode,
int endp)
{
if (!validate_arglist (arglist,
unsigned int dest_align
= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
rtx dest_mem, src_mem, len_rtx;
+ tree result = fold_builtin_mempcpy (arglist, type, endp);
- /* If DEST is not a pointer type, call the normal function. */
- if (dest_align == 0)
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
+
+ /* If either SRC or DEST is not a pointer type, don't do this
+ operation in-line. */
+ if (dest_align == 0 || src_align == 0)
return 0;
- /* If SRC and DEST are the same (and not volatile), do nothing. */
- if (operand_equal_p (src, dest, 0))
- {
- tree expr;
-
- 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);
- }
-
- if (endp == 2)
- len = fold (build2 (MINUS_EXPR, TREE_TYPE (len), len,
- integer_one_node));
- len = fold_convert (TREE_TYPE (dest), len);
- expr = fold (build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len));
- return expand_expr (expr, 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 we failed the caller should emit a normal call. */
static rtx
-expand_builtin_memmove (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_memmove (tree arglist, tree type, rtx target,
+ enum machine_mode mode, tree orig_exp)
{
if (!validate_arglist (arglist,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
unsigned int dest_align
= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
+ tree result = fold_builtin_memmove (arglist, type);
+
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
/* If DEST is not a pointer type, call the normal function. */
if (dest_align == 0)
return 0;
- /* If the LEN parameter is zero, return DEST. */
- 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)
normal memcpy. */
if (readonly_data_expr (src))
{
- tree const fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
+ 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);
+ fn = build_function_call_expr (fn, arglist);
+ if (TREE_CODE (fn) == CALL_EXPR)
+ CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (orig_exp);
+ return expand_expr (fn, target, mode, EXPAND_NORMAL);
}
+ /* If length is 1 and we can expand memcpy call inline,
+ it is ok to use memcpy as well. */
+ if (integer_onep (len))
+ {
+ rtx ret = expand_builtin_mempcpy (arglist, type, target, mode,
+ /*endp=*/0);
+ if (ret)
+ return ret;
+ }
+
/* Otherwise, call the normal function. */
return 0;
}
if we failed the caller should emit a normal call. */
static rtx
-expand_builtin_bcopy (tree arglist)
+expand_builtin_bcopy (tree exp)
{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree type = TREE_TYPE (exp);
tree src, dest, size, newarglist;
if (!validate_arglist (arglist,
newarglist = tree_cons (NULL_TREE, src, newarglist);
newarglist = tree_cons (NULL_TREE, dest, newarglist);
- return expand_builtin_memmove (newarglist, const0_rtx, VOIDmode);
+ return expand_builtin_memmove (newarglist, type, const0_rtx, VOIDmode, exp);
}
#ifndef HAVE_movstr
terminator. If the caller requested a mempcpy-like return value,
adjust it. */
if (endp == 1 && target != const0_rtx)
- emit_move_insn (target, plus_constant (gen_lowpart (GET_MODE (target),
- end), 1));
+ {
+ rtx tem = plus_constant (gen_lowpart (GET_MODE (target), end), 1);
+ emit_move_insn (target, force_operand (tem, NULL_RTX));
+ }
return target;
}
convenient). */
static rtx
-expand_builtin_strcpy (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_strcpy (tree exp, rtx target, enum machine_mode mode)
{
- 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);
-
- len = c_strlen (src, 1);
- if (len == 0 || TREE_SIDE_EFFECTS (len))
- return expand_movstr (TREE_VALUE (arglist),
- TREE_VALUE (TREE_CHAIN (arglist)),
- target, /*endp=*/0);
-
- fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
- if (!fn)
- return 0;
+ tree fndecl = get_callee_fndecl (exp);
+ tree arglist = TREE_OPERAND (exp, 1);
+ if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+ {
+ tree result = fold_builtin_strcpy (fndecl, arglist, 0);
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
- len = size_binop (PLUS_EXPR, len, ssize_int (1));
- 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);
+ return expand_movstr (TREE_VALUE (arglist),
+ TREE_VALUE (TREE_CHAIN (arglist)),
+ target, /*endp=*/0);
+ }
+ return 0;
}
/* Expand a call to the stpcpy builtin, with arguments in ARGLIST.
mode MODE if that's convenient). */
static rtx
-expand_builtin_stpcpy (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_stpcpy (tree exp, rtx target, enum machine_mode mode)
{
+ tree arglist = TREE_OPERAND (exp, 1);
/* If return value is ignored, transform stpcpy into strcpy. */
if (target == const0_rtx)
{
narglist = build_tree_list (NULL_TREE, lenp1);
narglist = tree_cons (NULL_TREE, src, narglist);
narglist = tree_cons (NULL_TREE, dst, narglist);
- ret = expand_builtin_mempcpy (narglist, target, mode, /*endp=*/2);
+ ret = expand_builtin_mempcpy (narglist, TREE_TYPE (exp),
+ target, mode, /*endp=*/2);
if (ret)
return ret;
if (GET_CODE (len_rtx) == CONST_INT)
{
- ret = expand_builtin_strcpy (arglist, target, mode);
+ ret = expand_builtin_strcpy (exp, target, mode);
if (ret)
{
if (GET_MODE (target) != GET_MODE (ret))
ret = gen_lowpart (GET_MODE (target), ret);
- ret = emit_move_insn (target,
- plus_constant (ret,
- INTVAL (len_rtx)));
+ ret = plus_constant (ret, INTVAL (len_rtx));
+ ret = emit_move_insn (target, force_operand (ret, NULL_RTX));
gcc_assert (ret);
return target;
if we failed the caller should emit a normal call. */
static rtx
-expand_builtin_strncpy (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_strncpy (tree exp, rtx target, enum machine_mode mode)
{
- if (!validate_arglist (arglist,
- POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
- return 0;
- else
+ tree fndecl = get_callee_fndecl (exp);
+ tree arglist = TREE_OPERAND (exp, 1);
+ if (validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
{
tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 1);
tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
- tree fn;
-
- /* We must be passed a constant len parameter. */
- if (TREE_CODE (len) != INTEGER_CST)
- return 0;
-
- /* If the len parameter is zero, return the dst parameter. */
- if (integer_zerop (len))
- {
- /* Evaluate and ignore the src argument in case it has
- side-effects. */
- expand_expr (TREE_VALUE (TREE_CHAIN (arglist)), const0_rtx,
- VOIDmode, EXPAND_NORMAL);
- /* Return the dst parameter. */
- return expand_expr (TREE_VALUE (arglist), target, mode,
- EXPAND_NORMAL);
- }
+ tree result = fold_builtin_strncpy (fndecl, arglist, slen);
+
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
- /* Now, we must be passed a constant src ptr parameter. */
- if (slen == 0 || TREE_CODE (slen) != INTEGER_CST)
+ /* We must be passed a constant len and src parameter. */
+ if (!host_integerp (len, 1) || !slen || !host_integerp (slen, 1))
return 0;
slen = size_binop (PLUS_EXPR, slen, ssize_int (1));
dest_mem = convert_memory_address (ptr_mode, dest_mem);
return dest_mem;
}
-
- /* OK transform into builtin memcpy. */
- 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);
}
+ return 0;
}
/* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE)
convenient). */
static rtx
-expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_memset (tree arglist, rtx target, enum machine_mode mode,
+ tree orig_exp)
{
if (!validate_arglist (arglist,
POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
dest_mem = get_memory_rtx (dest);
set_mem_align (dest_mem, dest_align);
- dest_addr = clear_storage (dest_mem, len_rtx);
+ dest_addr = clear_storage (dest_mem, len_rtx,
+ CALL_EXPR_TAILCALL (orig_exp)
+ ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL);
if (dest_addr == 0)
{
if we failed the caller should emit a normal call. */
static rtx
-expand_builtin_bzero (tree arglist)
+expand_builtin_bzero (tree exp)
{
+ tree arglist = TREE_OPERAND (exp, 1);
tree dest, size, newarglist;
if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
newarglist = tree_cons (NULL_TREE, integer_zero_node, newarglist);
newarglist = tree_cons (NULL_TREE, dest, newarglist);
- return expand_builtin_memset (newarglist, const0_rtx, VOIDmode);
+ return expand_builtin_memset (newarglist, const0_rtx, VOIDmode, exp);
}
/* Expand expression EXP, which is a call to the memcmp built-in function.
expand_builtin_memcmp (tree exp ATTRIBUTE_UNUSED, tree arglist, rtx target,
enum machine_mode mode)
{
- tree arg1, arg2, len;
- const char *p1, *p2;
-
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))
- {
- /* Evaluate and ignore arg1 and arg2 in case they have
- side-effects. */
- expand_expr (arg1, const0_rtx, VOIDmode, EXPAND_NORMAL);
- expand_expr (arg2, const0_rtx, VOIDmode, EXPAND_NORMAL);
- 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 all arguments are constant, and the value of len is not greater
- than the lengths of arg1 and arg2, evaluate at compile-time. */
- if (host_integerp (len, 1) && p1 && p2
- && compare_tree_int (len, strlen (p1) + 1) <= 0
- && compare_tree_int (len, strlen (p2) + 1) <= 0)
- {
- const int r = memcmp (p1, p2, tree_low_cst (len, 1));
-
- return (r < 0 ? constm1_rtx : (r > 0 ? const1_rtx : const0_rtx));
- }
-
- /* If len parameter is one, return an expression corresponding to
- (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */
- if (integer_onep (len))
+ else
{
- tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
- tree ind1 =
- fold (build1 (CONVERT_EXPR, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert (cst_uchar_ptr_node, arg1))));
- tree ind2 =
- fold (build1 (CONVERT_EXPR, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert (cst_uchar_ptr_node, arg2))));
- tree result = fold (build2 (MINUS_EXPR, integer_type_node, ind1, ind2));
- return expand_expr (result, target, mode, EXPAND_NORMAL);
+ tree result = fold_builtin_memcmp (arglist);
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
}
#if defined HAVE_cmpmemsi || defined HAVE_cmpstrsi
{
+ tree arg1 = TREE_VALUE (arglist);
+ tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+ tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
rtx arg1_rtx, arg2_rtx, arg3_rtx;
rtx result;
rtx insn;
arg1_rtx = get_memory_rtx (arg1);
arg2_rtx = get_memory_rtx (arg2);
arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+
+ /* Set MEM_SIZE as appropriate. */
+ if (GET_CODE (arg3_rtx) == CONST_INT)
+ {
+ set_mem_size (arg1_rtx, arg3_rtx);
+ set_mem_size (arg2_rtx, arg3_rtx);
+ }
+
#ifdef HAVE_cmpmemsi
if (HAVE_cmpmemsi)
insn = gen_cmpmemsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
expand_builtin_strcmp (tree exp, rtx target, enum machine_mode mode)
{
tree arglist = TREE_OPERAND (exp, 1);
- tree arg1, arg2;
- const char *p1, *p2;
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
-
- 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);
-
- if (p1 && p2)
- {
- const int i = strcmp (p1, p2);
- return (i < 0 ? constm1_rtx : (i > 0 ? const1_rtx : const0_rtx));
- }
-
- /* If either arg is "", return an expression corresponding to
- (*(const unsigned char*)arg1 - (const unsigned char*)arg2). */
- if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
+ else
{
- tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
- tree ind1 =
- fold (build1 (CONVERT_EXPR, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert (cst_uchar_ptr_node, arg1))));
- tree ind2 =
- fold (build1 (CONVERT_EXPR, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert (cst_uchar_ptr_node, arg2))));
- tree result = fold (build2 (MINUS_EXPR, integer_type_node, ind1, ind2));
- return expand_expr (result, target, mode, EXPAND_NORMAL);
+ tree result = fold_builtin_strcmp (arglist);
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
}
#ifdef HAVE_cmpstrsi
if (HAVE_cmpstrsi)
{
+ tree arg1 = TREE_VALUE (arglist);
+ tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
tree len, len1, len2;
rtx arg1_rtx, arg2_rtx, arg3_rtx;
rtx result, insn;
- tree fndecl;
+ tree fndecl, fn;
int arg1_align
= get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
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);
+ fn = build_function_call_expr (fndecl, arglist);
+ if (TREE_CODE (fn) == CALL_EXPR)
+ CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+ return expand_call (fn, target, target == const0_rtx);
}
#endif
return 0;
expand_builtin_strncmp (tree exp, rtx target, enum machine_mode mode)
{
tree arglist = TREE_OPERAND (exp, 1);
- tree arg1, arg2, arg3;
- const char *p1, *p2;
if (!validate_arglist (arglist,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
-
- arg1 = TREE_VALUE (arglist);
- arg2 = TREE_VALUE (TREE_CHAIN (arglist));
- arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
-
- /* If the len parameter is zero, return zero. */
- if (integer_zerop (arg3))
- {
- /* Evaluate and ignore arg1 and arg2 in case they have
- side-effects. */
- expand_expr (arg1, const0_rtx, VOIDmode, EXPAND_NORMAL);
- expand_expr (arg2, const0_rtx, VOIDmode, EXPAND_NORMAL);
- 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);
-
- /* If all arguments are constant, evaluate at compile-time. */
- if (host_integerp (arg3, 1) && p1 && p2)
- {
- const int r = strncmp (p1, p2, tree_low_cst (arg3, 1));
- return (r < 0 ? constm1_rtx : (r > 0 ? const1_rtx : const0_rtx));
- }
-
- /* If len == 1 or (either string parameter is "" and (len >= 1)),
- return (*(const u_char*)arg1 - *(const u_char*)arg2). */
- if (host_integerp (arg3, 1)
- && (tree_low_cst (arg3, 1) == 1
- || (tree_low_cst (arg3, 1) > 1
- && ((p1 && *p1 == '\0') || (p2 && *p2 == '\0')))))
+ else
{
- tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
- tree ind1 =
- fold (build1 (CONVERT_EXPR, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert (cst_uchar_ptr_node, arg1))));
- tree ind2 =
- fold (build1 (CONVERT_EXPR, integer_type_node,
- build1 (INDIRECT_REF, cst_uchar_node,
- fold_convert (cst_uchar_ptr_node, arg2))));
- tree result = fold (build2 (MINUS_EXPR, integer_type_node, ind1, ind2));
- return expand_expr (result, target, mode, EXPAND_NORMAL);
+ tree result = fold_builtin_strncmp (arglist);
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
}
/* If c_strlen can determine an expression for one of the string
#ifdef HAVE_cmpstrsi
if (HAVE_cmpstrsi)
{
+ tree arg1 = TREE_VALUE (arglist);
+ tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+ tree arg3 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
tree len, len1, len2;
rtx arg1_rtx, arg2_rtx, arg3_rtx;
rtx result, insn;
- tree fndecl;
+ tree fndecl, fn;
int arg1_align
= get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
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);
+ fn = build_function_call_expr (fndecl, arglist);
+ if (TREE_CODE (fn) == CALL_EXPR)
+ CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+ return expand_call (fn, target, target == const0_rtx);
}
#endif
return 0;
otherwise try to get the result in TARGET, if convenient. */
static rtx
-expand_builtin_strcat (tree arglist, rtx target, enum machine_mode mode)
+expand_builtin_strcat (tree arglist, tree type, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
fold (build_function_call_expr (strlen_fn,
build_tree_list (NULL_TREE,
dst)));
- /* Create (dst + strlen (dst)). */
+ /* Create (dst + (cast) strlen (dst)). */
+ newdst = fold_convert (TREE_TYPE (dst), newdst);
newdst = fold (build2 (PLUS_EXPR, TREE_TYPE (dst), dst, newdst));
/* Prepend the new dst argument. */
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))
+ if (expand_builtin_mempcpy (arglist, type, /*target=*/0, mode, /*endp=*/0))
return expand_expr (dst, target, mode, EXPAND_NORMAL);
else
return 0;
static rtx
expand_builtin_strncat (tree arglist, rtx target, enum machine_mode mode)
{
- if (!validate_arglist (arglist,
- POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
- return 0;
- else
+ if (validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
{
- tree dst = TREE_VALUE (arglist),
- src = TREE_VALUE (TREE_CHAIN (arglist)),
- len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
- const char *p = c_getstr (src);
-
- /* If the requested length is zero, or the src parameter string
- length is zero, return the dst parameter. */
- if (integer_zerop (len) || (p && *p == '\0'))
- {
- /* Evaluate and ignore the src and len parameters in case
- they have side-effects. */
- expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
- expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
- return expand_expr (dst, target, mode, EXPAND_NORMAL);
- }
-
- /* If the requested len is greater than or equal to the string
- length, call strcat. */
- if (TREE_CODE (len) == INTEGER_CST && p
- && compare_tree_int (len, strlen (p)) >= 0)
- {
- tree newarglist
- = tree_cons (NULL_TREE, dst, build_tree_list (NULL_TREE, src));
- tree fn = implicit_built_in_decls[BUILT_IN_STRCAT];
-
- /* If the replacement _DECL isn't initialized, don't do the
- transformation. */
- if (!fn)
- return 0;
-
- return expand_expr (build_function_call_expr (fn, newarglist),
- target, mode, EXPAND_NORMAL);
- }
- return 0;
+ tree result = fold_builtin_strncat (arglist);
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
}
+ return 0;
}
/* Expand expression EXP, which is a call to the strspn builtin.
static rtx
expand_builtin_strspn (tree arglist, rtx target, enum machine_mode mode)
{
- if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
- return 0;
- else
+ if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
{
- tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
- const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
-
- /* If both arguments are constants, evaluate at compile-time. */
- if (p1 && p2)
- {
- const size_t r = strspn (p1, p2);
- return expand_expr (size_int (r), target, mode, EXPAND_NORMAL);
- }
-
- /* If either argument is "", return 0. */
- if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
- {
- /* Evaluate and ignore both arguments in case either one has
- side-effects. */
- expand_expr (s1, const0_rtx, VOIDmode, EXPAND_NORMAL);
- expand_expr (s2, const0_rtx, VOIDmode, EXPAND_NORMAL);
- return const0_rtx;
- }
- return 0;
+ tree result = fold_builtin_strspn (arglist);
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
}
+ return 0;
}
/* Expand expression EXP, which is a call to the strcspn builtin.
static rtx
expand_builtin_strcspn (tree arglist, rtx target, enum machine_mode mode)
{
- if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
- return 0;
- else
+ if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
{
- tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
- const char *p1 = c_getstr (s1), *p2 = c_getstr (s2);
-
- /* If both arguments are constants, evaluate at compile-time. */
- if (p1 && p2)
- {
- const size_t r = strcspn (p1, p2);
- return expand_expr (size_int (r), target, mode, EXPAND_NORMAL);
- }
-
- /* If the first argument is "", return 0. */
- if (p1 && *p1 == '\0')
- {
- /* Evaluate and ignore argument s2 in case it has
- side-effects. */
- expand_expr (s2, const0_rtx, VOIDmode, EXPAND_NORMAL);
- return const0_rtx;
- }
-
- /* If the second argument is "", return __builtin_strlen(s1). */
- if (p2 && *p2 == '\0')
- {
- tree newarglist = build_tree_list (NULL_TREE, s1),
- fn = implicit_built_in_decls[BUILT_IN_STRLEN];
-
- /* If the replacement _DECL isn't initialized, don't do the
- transformation. */
- if (!fn)
- return 0;
-
- return expand_expr (build_function_call_expr (fn, newarglist),
- target, mode, EXPAND_NORMAL);
- }
- return 0;
+ tree result = fold_builtin_strcspn (arglist);
+ if (result)
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
}
+ return 0;
}
/* Expand a call to __builtin_saveregs, generating the result in TARGET,
if (arglist != 0)
{
if (!host_integerp (TREE_VALUE (arglist), 0))
- error ("argument of `__builtin_args_info' must be constant");
+ error ("argument of %<__builtin_args_info%> must be constant");
else
{
HOST_WIDE_INT wordnum = tree_low_cst (TREE_VALUE (arglist), 0);
if (wordnum < 0 || wordnum >= nwords)
- error ("argument of `__builtin_args_info' out of range");
+ error ("argument of %<__builtin_args_info%> out of range");
else
return GEN_INT (word_ptr[wordnum]);
}
}
else
- error ("missing argument in `__builtin_args_info'");
+ error ("missing argument in %<__builtin_args_info%>");
return const0_rtx;
}
-/* Expand ARGLIST, from a call to __builtin_next_arg. */
+/* Expand a call to __builtin_next_arg. */
static rtx
-expand_builtin_next_arg (tree arglist)
+expand_builtin_next_arg (void)
{
- tree fntype = TREE_TYPE (current_function_decl);
-
- if (TYPE_ARG_TYPES (fntype) == 0
- || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
- == void_type_node))
- {
- error ("`va_start' used in function with fixed args");
- return const0_rtx;
- }
-
- if (arglist)
- {
- tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl));
- tree arg = TREE_VALUE (arglist);
-
- /* Strip off all nops for the sake of the comparison. This
- is not quite the same as STRIP_NOPS. It does more.
- We must also strip off INDIRECT_EXPR for C++ reference
- parameters. */
- while (TREE_CODE (arg) == NOP_EXPR
- || TREE_CODE (arg) == CONVERT_EXPR
- || TREE_CODE (arg) == NON_LVALUE_EXPR
- || TREE_CODE (arg) == INDIRECT_REF)
- arg = TREE_OPERAND (arg, 0);
- if (arg != last_parm)
- warning ("second parameter of `va_start' not last named argument");
- }
- else
- /* Evidently an out of date version of <stdarg.h>; can't validate
- va_start's second argument, but can still work as intended. */
- warning ("`__builtin_next_arg' called without an argument");
-
+ /* Checking arguments is already done in fold_builtin_next_arg
+ that must be called before this function. */
return expand_binop (Pmode, add_optab,
current_function_internal_arg_pointer,
current_function_arg_offset_rtx,
chain = TREE_CHAIN (arglist);
- if (TREE_CHAIN (chain))
- error ("too many arguments to function `va_start'");
+ if (!chain)
+ {
+ error ("too few arguments to function %<va_start%>");
+ return const0_rtx;
+ }
+
+ if (fold_builtin_next_arg (chain))
+ return const0_rtx;
- nextarg = expand_builtin_next_arg (chain);
+ nextarg = expand_builtin_next_arg ();
valist = stabilize_va_list (TREE_VALUE (arglist), 1);
#ifdef EXPAND_BUILTIN_VA_START
build2 (BIT_AND_EXPR, TREE_TYPE (valist), valist_tmp, t));
gimplify_and_add (t, pre_p);
}
+ else
+ boundary = align;
+
+ /* If the actual alignment is less than the alignment of the type,
+ adjust the type accordingly so that we don't assume strict alignment
+ when deferencing the pointer. */
+ boundary *= BITS_PER_UNIT;
+ if (boundary < TYPE_ALIGN (type))
+ {
+ type = build_variant_type_copy (type);
+ TYPE_ALIGN (type) = boundary;
+ }
/* Compute the rounded size of the type. */
type_size = size_in_bytes (type);
In that case, unwrap both types so that we can compare the
underlying records. */
if (TREE_CODE (have_va_type) == ARRAY_TYPE
- || TREE_CODE (have_va_type) == POINTER_TYPE)
+ || POINTER_TYPE_P (have_va_type))
{
want_va_type = TREE_TYPE (want_va_type);
have_va_type = TREE_TYPE (have_va_type);
if (TYPE_MAIN_VARIANT (want_va_type) != TYPE_MAIN_VARIANT (have_va_type))
{
- error ("first argument to `va_arg' not of type `va_list'");
+ error ("first argument to %<va_arg%> not of type %<va_list%>");
return GS_ERROR;
}
/* Unfortunately, this is merely undefined, rather than a constraint
violation, so we cannot make this an error. If this call is never
executed, the program is still strictly conforming. */
- warning ("`%T' is promoted to `%T' when passed through `...'",
+ warning (0, "%qT is promoted to %qT when passed through %<...%>",
type, promoted_type);
if (! gave_help)
{
gave_help = true;
- warning ("(so you should pass `%T' not `%T' to `va_arg')",
+ warning (0, "(so you should pass %qT not %qT to %<va_arg%>)",
promoted_type, type);
}
gimplify_expr (&valist, pre_p, post_p, is_gimple_min_lval, fb_lvalue);
if (!targetm.gimplify_va_arg_expr)
- /* Once most targets are converted this should abort. */
+ /* FIXME:Once most targets are converted we should merely
+ assert this is non-null. */
return GS_ALL_DONE;
*expr_p = targetm.gimplify_va_arg_expr (valist, type, pre_p, post_p);
else if (! host_integerp (TREE_VALUE (arglist), 1))
{
if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
- error ("invalid arg to `__builtin_frame_address'");
+ error ("invalid argument to %<__builtin_frame_address%>");
else
- error ("invalid arg to `__builtin_return_address'");
+ error ("invalid argument to %<__builtin_return_address%>");
return const0_rtx;
}
else
{
rtx tem
= expand_builtin_return_addr (DECL_FUNCTION_CODE (fndecl),
- tree_low_cst (TREE_VALUE (arglist), 1),
- hard_frame_pointer_rtx);
+ tree_low_cst (TREE_VALUE (arglist), 1));
/* Some ports cannot access arbitrary stack frames. */
if (tem == NULL)
{
if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
- warning ("unsupported arg to `__builtin_frame_address'");
+ warning (0, "unsupported argument to %<__builtin_frame_address%>");
else
- warning ("unsupported arg to `__builtin_return_address'");
+ warning (0, "unsupported argument to %<__builtin_return_address%>");
return const0_rtx;
}
static rtx
expand_builtin_fputs (tree arglist, rtx target, bool unlocked)
{
- tree len, fn;
- tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
- : implicit_built_in_decls[BUILT_IN_FPUTC];
- tree fn_fwrite = unlocked ? implicit_built_in_decls[BUILT_IN_FWRITE_UNLOCKED]
- : implicit_built_in_decls[BUILT_IN_FWRITE];
-
- /* If the return value is used, or the replacement _DECL isn't
- initialized, don't do the transformation. */
- if (target != const0_rtx || !fn_fputc || !fn_fwrite)
- return 0;
-
/* Verify the arguments in the original call. */
- if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
- return 0;
-
- /* Get the length of the string passed to fputs. If the length
- can't be determined, punt. */
- if (!(len = c_strlen (TREE_VALUE (arglist), 1))
- || TREE_CODE (len) != INTEGER_CST)
- return 0;
-
- switch (compare_tree_int (len, 1))
+ if (validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
{
- case -1: /* length is 0, delete the call entirely . */
- {
- /* Evaluate and ignore the argument in case it has
- side-effects. */
- expand_expr (TREE_VALUE (TREE_CHAIN (arglist)), const0_rtx,
- VOIDmode, EXPAND_NORMAL);
- return const0_rtx;
- }
- case 0: /* length is 1, call fputc. */
- {
- const char *p = c_getstr (TREE_VALUE (arglist));
-
- if (p != NULL)
- {
- /* New argument list transforming fputs(string, stream) to
- fputc(string[0], stream). */
- arglist = build_tree_list (NULL_TREE,
- TREE_VALUE (TREE_CHAIN (arglist)));
- arglist = tree_cons (NULL_TREE,
- build_int_cst (NULL_TREE, p[0]),
- arglist);
- fn = fn_fputc;
- break;
- }
- }
- /* Fall through. */
- case 1: /* length is greater than 1, call fwrite. */
- {
- tree string_arg;
-
- /* If optimizing for size keep fputs. */
- if (optimize_size)
- return 0;
- string_arg = TREE_VALUE (arglist);
- /* New argument list transforming fputs(string, stream) to
- fwrite(string, 1, len, stream). */
- arglist = build_tree_list (NULL_TREE, TREE_VALUE (TREE_CHAIN (arglist)));
- arglist = tree_cons (NULL_TREE, len, arglist);
- arglist = tree_cons (NULL_TREE, size_one_node, arglist);
- arglist = tree_cons (NULL_TREE, string_arg, arglist);
- fn = fn_fwrite;
- break;
- }
- default:
- gcc_unreachable ();
+ tree result = fold_builtin_fputs (arglist, (target == const0_rtx),
+ unlocked, NULL_TREE);
+ if (result)
+ return expand_expr (result, target, VOIDmode, EXPAND_NORMAL);
}
-
- return expand_expr (build_function_call_expr (fn, arglist),
- const0_rtx, VOIDmode, EXPAND_NORMAL);
+ return 0;
}
/* Expand a call to __builtin_expect. We return our argument and emit a
if (TREE_CODE (c) != INTEGER_CST)
{
- error ("second arg to `__builtin_expect' must be a constant");
+ error ("second argument to %<__builtin_expect%> must be a constant");
c = integer_zero_node;
}
return ret;
}
-void
+static void
expand_builtin_trap (void)
{
#ifdef HAVE_trap
return expand_abs (mode, op0, target, 0, safe_from_p (target, arg, 1));
}
+/* Expand a call to copysign, copysignf, or copysignl with arguments ARGLIST.
+ Return NULL is 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. */
+
+static rtx
+expand_builtin_copysign (tree arglist, rtx target, rtx subtarget)
+{
+ rtx op0, op1;
+ tree arg;
+
+ if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ arg = TREE_VALUE (arglist);
+ op0 = expand_expr (arg, subtarget, VOIDmode, 0);
+
+ arg = TREE_VALUE (TREE_CHAIN (arglist));
+ op1 = expand_expr (arg, NULL, VOIDmode, 0);
+
+ return expand_copysign (op0, op1, target);
+}
+
/* Create a new constant string literal and return a char* pointer to it.
The STRING_CST value is the LEN characters at STR. */
static tree
return t;
}
-/* Expand a call to printf or printf_unlocked with argument list ARGLIST.
+/* Expand EXP, a call to printf or printf_unlocked.
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,
+expand_builtin_printf (tree exp, rtx target, enum machine_mode mode,
bool unlocked)
{
+ tree arglist = TREE_OPERAND (exp, 1);
tree fn_putchar = unlocked
? implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]
: implicit_built_in_decls[BUILT_IN_PUTCHAR];
if (! arglist)
return 0;
fmt = TREE_VALUE (arglist);
- if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+ if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
return 0;
arglist = TREE_CHAIN (arglist);
if (strcmp (fmt_str, "%s\n") == 0)
{
if (! arglist
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
|| TREE_CHAIN (arglist))
return 0;
fn = fn_puts;
if (!fn)
return 0;
- return expand_expr (build_function_call_expr (fn, arglist),
- target, mode, EXPAND_NORMAL);
+ fn = build_function_call_expr (fn, arglist);
+ if (TREE_CODE (fn) == CALL_EXPR)
+ CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+ return expand_expr (fn, target, mode, EXPAND_NORMAL);
}
-/* Expand a call to fprintf or fprintf_unlocked with argument list ARGLIST.
+/* Expand EXP, a call to fprintf or fprintf_unlocked.
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,
+expand_builtin_fprintf (tree exp, rtx target, enum machine_mode mode,
bool unlocked)
{
+ tree arglist = TREE_OPERAND (exp, 1);
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]
if (! arglist)
return 0;
fp = TREE_VALUE (arglist);
- if (TREE_CODE (TREE_TYPE (fp)) != POINTER_TYPE)
+ if (! POINTER_TYPE_P (TREE_TYPE (fp)))
return 0;
arglist = TREE_CHAIN (arglist);
if (! arglist)
return 0;
fmt = TREE_VALUE (arglist);
- if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+ if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
return 0;
arglist = TREE_CHAIN (arglist);
if (strcmp (fmt_str, "%s") == 0)
{
if (! arglist
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
|| TREE_CHAIN (arglist))
return 0;
arg = TREE_VALUE (arglist);
if (!fn)
return 0;
- return expand_expr (build_function_call_expr (fn, arglist),
- target, mode, EXPAND_NORMAL);
+ fn = build_function_call_expr (fn, arglist);
+ if (TREE_CODE (fn) == CALL_EXPR)
+ CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+ return expand_expr (fn, target, mode, EXPAND_NORMAL);
}
/* Expand a call to sprintf with argument list ARGLIST. Return 0 if
if (! arglist)
return 0;
dest = TREE_VALUE (arglist);
- if (TREE_CODE (TREE_TYPE (dest)) != POINTER_TYPE)
+ if (! POINTER_TYPE_P (TREE_TYPE (dest)))
return 0;
arglist = TREE_CHAIN (arglist);
if (! arglist)
return 0;
fmt = TREE_VALUE (arglist);
- if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+ if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
return 0;
arglist = TREE_CHAIN (arglist);
if (! arglist || TREE_CHAIN (arglist))
return 0;
arg = TREE_VALUE (arglist);
- if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
+ if (! POINTER_TYPE_P (TREE_TYPE (arg)))
return 0;
if (target != const0_rtx)
emit_library_call (which, LCT_NORMAL, VOIDmode, 2, this, Pmode,
expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
- 0, hard_frame_pointer_rtx),
+ 0),
Pmode);
return const0_rtx;
enum machine_mode fmode, imode, rmode;
HOST_WIDE_INT hi, lo;
tree arg, arglist;
- int bitpos;
+ int word, bitpos;
rtx temp;
arglist = TREE_OPERAND (exp, 1);
/* For floating point formats without a sign bit, implement signbit
as "ARG < 0.0". */
- if (fmt->signbit < 0)
+ bitpos = fmt->signbit_ro;
+ if (bitpos < 0)
{
/* But we can't do this if the format supports signed zero. */
if (fmt->has_signed_zero && HONOR_SIGNED_ZEROS (fmode))
return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL);
}
- imode = int_mode_for_mode (fmode);
- if (imode == BLKmode)
- return 0;
-
- bitpos = fmt->signbit;
- /* Handle targets with different FP word orders. */
- if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+ temp = expand_expr (arg, NULL_RTX, VOIDmode, 0);
+ if (GET_MODE_SIZE (fmode) <= UNITS_PER_WORD)
+ {
+ imode = int_mode_for_mode (fmode);
+ if (imode == BLKmode)
+ return 0;
+ temp = gen_lowpart (imode, temp);
+ }
+ else
{
- 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;
+ imode = word_mode;
+ /* Handle targets with different FP word orders. */
+ if (FLOAT_WORDS_BIG_ENDIAN)
+ word = (GET_MODE_BITSIZE (fmode) - bitpos) / BITS_PER_WORD;
+ else
+ word = bitpos / BITS_PER_WORD;
+ temp = operand_subword_force (temp, word, fmode);
+ bitpos = bitpos % BITS_PER_WORD;
}
- /* 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;
+ /* Force the intermediate word_mode (or narrower) result into a
+ register. This avoids attempting to create paradoxical SUBREGs
+ of floating point modes below. */
+ temp = force_reg (imode, temp);
- temp = expand_expr (arg, NULL_RTX, VOIDmode, 0);
- temp = gen_lowpart (imode, temp);
+ /* If the bitpos is within the "result mode" lowpart, the operation
+ can be implement with a single bitwise AND. Otherwise, we need
+ a right shift and an AND. */
- if (GET_MODE_BITSIZE (imode) > GET_MODE_BITSIZE (rmode))
- {
- if (BYTES_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);
- }
- else
+ if (bitpos < GET_MODE_BITSIZE (rmode))
{
- if (GET_MODE_BITSIZE (imode) < GET_MODE_BITSIZE (rmode))
- temp = gen_lowpart (rmode, temp);
if (bitpos < HOST_BITS_PER_WIDE_INT)
{
hi = 0;
lo = 0;
}
- temp = force_reg (rmode, temp);
+ if (imode != rmode)
+ temp = gen_lowpart (rmode, temp);
temp = expand_binop (rmode, and_optab, temp,
immed_double_const (lo, hi, rmode),
- target, 1, OPTAB_LIB_WIDEN);
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
}
+ else
+ {
+ /* Perform a logical right shift to place the signbit in the least
+ significant bit, then truncate the result to the desired mode
+ and mask just this bit. */
+ temp = expand_shift (RSHIFT_EXPR, imode, temp,
+ build_int_cst (NULL_TREE, bitpos), NULL_RTX, 1);
+ temp = gen_lowpart (rmode, temp);
+ temp = expand_binop (rmode, and_optab, temp, const1_rtx,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ }
+
return temp;
}
return expand_call (call, target, ignore);
}
+
+\f
+/* Expand the __sync_xxx_and_fetch and __sync_fetch_and_xxx intrinsics.
+ ARGLIST is the operands list to the function. CODE is the rtx code
+ that corresponds to the arithmetic or logical operation from the name;
+ an exception here is that NOT actually means NAND. TARGET is an optional
+ place for us to store the results; AFTER is true if this is the
+ fetch_and_xxx form. IGNORE is true if we don't actually care about
+ the result of the operation at all. */
+
+static rtx
+expand_builtin_sync_operation (tree arglist, enum rtx_code code, bool after,
+ rtx target, bool ignore)
+{
+ enum machine_mode mode;
+ rtx addr, val, mem;
+
+ /* Expand the operands. */
+ addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_SUM);
+ mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist))));
+
+ arglist = TREE_CHAIN (arglist);
+ val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+ /* Note that we explicitly do not want any alias information for this
+ memory, so that we kill all other live memories. Otherwise we don't
+ satisfy the full barrier semantics of the intrinsic. */
+ mem = validize_mem (gen_rtx_MEM (mode, addr));
+ MEM_VOLATILE_P (mem) = 1;
+
+ if (ignore)
+ return expand_sync_operation (mem, val, code);
+ else
+ return expand_sync_fetch_operation (mem, val, code, after, target);
+}
+
+/* Expand the __sync_val_compare_and_swap and __sync_bool_compare_and_swap
+ intrinsics. ARGLIST is the operands list to the function. IS_BOOL is
+ true if this is the boolean form. TARGET is a place for us to store the
+ results; this is NOT optional if IS_BOOL is true. */
+
+static rtx
+expand_builtin_compare_and_swap (tree arglist, bool is_bool, rtx target)
+{
+ enum machine_mode mode;
+ rtx addr, old_val, new_val, mem;
+
+ /* Expand the operands. */
+ addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_SUM);
+ mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist))));
+
+ arglist = TREE_CHAIN (arglist);
+ old_val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+ arglist = TREE_CHAIN (arglist);
+ new_val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+ /* Note that we explicitly do not want any alias information for this
+ memory, so that we kill all other live memories. Otherwise we don't
+ satisfy the full barrier semantics of the intrinsic. */
+ mem = validize_mem (gen_rtx_MEM (mode, addr));
+ MEM_VOLATILE_P (mem) = 1;
+
+ if (is_bool)
+ return expand_bool_compare_and_swap (mem, old_val, new_val, target);
+ else
+ return expand_val_compare_and_swap (mem, old_val, new_val, target);
+}
+
+/* Expand the __sync_lock_test_and_set intrinsic. Note that the most
+ general form is actually an atomic exchange, and some targets only
+ support a reduced form with the second argument being a constant 1.
+ ARGLIST is the operands list to the function; TARGET is an optional
+ place for us to store the results. */
+
+static rtx
+expand_builtin_lock_test_and_set (tree arglist, rtx target)
+{
+ enum machine_mode mode;
+ rtx addr, val, mem;
+
+ /* Expand the operands. */
+ addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_NORMAL);
+ mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist))));
+
+ arglist = TREE_CHAIN (arglist);
+ val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+ /* Note that we explicitly do not want any alias information for this
+ memory, so that we kill all other live memories. Otherwise we don't
+ satisfy the barrier semantics of the intrinsic. */
+ mem = validize_mem (gen_rtx_MEM (mode, addr));
+ MEM_VOLATILE_P (mem) = 1;
+
+ return expand_sync_lock_test_and_set (mem, val, target);
+}
+
+/* Expand the __sync_synchronize intrinsic. */
+
+static void
+expand_builtin_synchronize (void)
+{
+ rtx body;
+
+#ifdef HAVE_memory_barrier
+ if (HAVE_memory_barrier)
+ {
+ emit_insn (gen_memory_barrier ());
+ return;
+ }
+#endif
+
+ /* If no explicit memory barrier instruction is available, create an empty
+ asm stmt that will prevent compiler movement across the barrier. */
+ body = gen_rtx_ASM_INPUT (VOIDmode, "");
+ MEM_VOLATILE_P (body) = 1;
+ emit_insn (body);
+}
+
+/* Expand the __sync_lock_release intrinsic. ARGLIST is the operands list
+ to the function. */
+
+static void
+expand_builtin_lock_release (tree arglist)
+{
+ enum machine_mode mode;
+ enum insn_code icode;
+ rtx addr, val, mem, insn;
+
+ /* Expand the operands. */
+ addr = expand_expr (TREE_VALUE (arglist), NULL, Pmode, EXPAND_NORMAL);
+ mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (TREE_VALUE (arglist))));
+ val = const0_rtx;
+
+ /* Note that we explicitly do not want any alias information for this
+ memory, so that we kill all other live memories. Otherwise we don't
+ satisfy the barrier semantics of the intrinsic. */
+ mem = validize_mem (gen_rtx_MEM (mode, addr));
+ MEM_VOLATILE_P (mem) = 1;
+
+ /* If there is an explicit operation in the md file, use it. */
+ icode = sync_lock_release[mode];
+ if (icode != CODE_FOR_nothing)
+ {
+ if (!insn_data[icode].operand[1].predicate (val, mode))
+ val = force_reg (mode, val);
+
+ insn = GEN_FCN (icode) (mem, val);
+ if (insn)
+ {
+ emit_insn (insn);
+ return;
+ }
+ }
+
+ /* Otherwise we can implement this operation by emitting a barrier
+ followed by a store of zero. */
+ expand_builtin_synchronize ();
+ emit_move_insn (mem, val);
+}
\f
/* Expand an expression EXP that calls a built-in function,
with result going to TARGET if that's convenient
/* When not optimizing, generate calls to library functions for a certain
set of builtins. */
if (!optimize
- && !CALLED_AS_BUILT_IN (fndecl)
+ && !called_as_built_in (fndecl)
&& DECL_ASSEMBLER_NAME_SET_P (fndecl)
&& fcode != BUILT_IN_ALLOCA)
return expand_call (exp, target, ignore);
case BUILT_IN_FABSL:
target = expand_builtin_fabs (arglist, target, subtarget);
if (target)
- return target;
+ return target;
+ break;
+
+ case BUILT_IN_COPYSIGN:
+ case BUILT_IN_COPYSIGNF:
+ case BUILT_IN_COPYSIGNL:
+ target = expand_builtin_copysign (arglist, target, subtarget);
+ if (target)
+ return target;
break;
/* Just do a normal library call if we were unable to fold
case BUILT_IN_RINT:
case BUILT_IN_RINTF:
case BUILT_IN_RINTL:
+ case BUILT_IN_LRINT:
+ case BUILT_IN_LRINTF:
+ case BUILT_IN_LRINTL:
+ case BUILT_IN_LLRINT:
+ case BUILT_IN_LLRINTF:
+ case BUILT_IN_LLRINTL:
target = expand_builtin_mathfn (exp, target, subtarget);
if (target)
return target;
break;
+ case BUILT_IN_LCEIL:
+ case BUILT_IN_LCEILF:
+ case BUILT_IN_LCEILL:
+ case BUILT_IN_LLCEIL:
+ case BUILT_IN_LLCEILF:
+ case BUILT_IN_LLCEILL:
+ case BUILT_IN_LFLOOR:
+ case BUILT_IN_LFLOORF:
+ case BUILT_IN_LFLOORL:
+ case BUILT_IN_LLFLOOR:
+ case BUILT_IN_LLFLOORF:
+ case BUILT_IN_LLFLOORL:
+ target = expand_builtin_int_roundingfn (exp, target, subtarget);
+ if (target)
+ return target;
+ break;
+
case BUILT_IN_POW:
case BUILT_IN_POWF:
case BUILT_IN_POWL:
return target;
break;
+ case BUILT_IN_POWI:
+ case BUILT_IN_POWIF:
+ case BUILT_IN_POWIL:
+ target = expand_builtin_powi (exp, target, subtarget);
+ if (target)
+ return target;
+ break;
+
case BUILT_IN_ATAN2:
case BUILT_IN_ATAN2F:
case BUILT_IN_ATAN2L:
+ case BUILT_IN_LDEXP:
+ case BUILT_IN_LDEXPF:
+ case BUILT_IN_LDEXPL:
case BUILT_IN_FMOD:
case BUILT_IN_FMODF:
case BUILT_IN_FMODL:
/* Return the address of the first anonymous stack arg. */
case BUILT_IN_NEXT_ARG:
- simplify_builtin_next_arg (arglist);
- return expand_builtin_next_arg (arglist);
+ if (fold_builtin_next_arg (arglist))
+ return const0_rtx;
+ return expand_builtin_next_arg ();
case BUILT_IN_CLASSIFY_TYPE:
return expand_builtin_classify_type (arglist);
break;
case BUILT_IN_STRCPY:
- target = expand_builtin_strcpy (arglist, target, mode);
+ target = expand_builtin_strcpy (exp, target, mode);
if (target)
return target;
break;
case BUILT_IN_STRNCPY:
- target = expand_builtin_strncpy (arglist, target, mode);
+ target = expand_builtin_strncpy (exp, target, mode);
if (target)
return target;
break;
case BUILT_IN_STPCPY:
- target = expand_builtin_stpcpy (arglist, target, mode);
+ target = expand_builtin_stpcpy (exp, target, mode);
if (target)
return target;
break;
case BUILT_IN_STRCAT:
- target = expand_builtin_strcat (arglist, target, mode);
+ target = expand_builtin_strcat (arglist, TREE_TYPE (exp), target, mode);
if (target)
return target;
break;
break;
case BUILT_IN_STRSTR:
- target = expand_builtin_strstr (arglist, target, mode);
+ target = expand_builtin_strstr (arglist, TREE_TYPE (exp), target, mode);
if (target)
return target;
break;
case BUILT_IN_STRPBRK:
- target = expand_builtin_strpbrk (arglist, target, mode);
+ target = expand_builtin_strpbrk (arglist, TREE_TYPE (exp), target, mode);
if (target)
return target;
break;
case BUILT_IN_INDEX:
case BUILT_IN_STRCHR:
- target = expand_builtin_strchr (arglist, target, mode);
+ target = expand_builtin_strchr (arglist, TREE_TYPE (exp), target, mode);
if (target)
return target;
break;
case BUILT_IN_RINDEX:
case BUILT_IN_STRRCHR:
- target = expand_builtin_strrchr (arglist, target, mode);
+ target = expand_builtin_strrchr (arglist, TREE_TYPE (exp), target, mode);
if (target)
return target;
break;
case BUILT_IN_MEMCPY:
- target = expand_builtin_memcpy (arglist, target, mode);
+ target = expand_builtin_memcpy (exp, target, mode);
if (target)
return target;
break;
case BUILT_IN_MEMPCPY:
- target = expand_builtin_mempcpy (arglist, target, mode, /*endp=*/ 1);
+ target = expand_builtin_mempcpy (arglist, TREE_TYPE (exp), target, mode, /*endp=*/ 1);
if (target)
return target;
break;
case BUILT_IN_MEMMOVE:
- target = expand_builtin_memmove (arglist, target, mode);
+ target = expand_builtin_memmove (arglist, TREE_TYPE (exp), target,
+ mode, exp);
if (target)
return target;
break;
case BUILT_IN_BCOPY:
- target = expand_builtin_bcopy (arglist);
+ target = expand_builtin_bcopy (exp);
if (target)
return target;
break;
case BUILT_IN_MEMSET:
- target = expand_builtin_memset (arglist, target, mode);
+ target = expand_builtin_memset (arglist, target, mode, exp);
if (target)
return target;
break;
case BUILT_IN_BZERO:
- target = expand_builtin_bzero (arglist);
+ target = expand_builtin_bzero (exp);
if (target)
return target;
break;
if (value != const1_rtx)
{
- error ("__builtin_longjmp second argument must be 1");
+ error ("%<__builtin_longjmp%> second argument must be 1");
return const0_rtx;
}
return const0_rtx;
case BUILT_IN_PRINTF:
- target = expand_builtin_printf (arglist, target, mode, false);
+ target = expand_builtin_printf (exp, target, mode, false);
if (target)
return target;
break;
case BUILT_IN_PRINTF_UNLOCKED:
- target = expand_builtin_printf (arglist, target, mode, true);
+ target = expand_builtin_printf (exp, target, mode, true);
if (target)
return target;
break;
break;
case BUILT_IN_FPRINTF:
- target = expand_builtin_fprintf (arglist, target, mode, false);
+ target = expand_builtin_fprintf (exp, target, mode, false);
if (target)
return target;
break;
case BUILT_IN_FPRINTF_UNLOCKED:
- target = expand_builtin_fprintf (arglist, target, mode, true);
+ target = expand_builtin_fprintf (exp, target, mode, true);
if (target)
return target;
break;
return target;
break;
+ case BUILT_IN_FETCH_AND_ADD_1:
+ case BUILT_IN_FETCH_AND_ADD_2:
+ case BUILT_IN_FETCH_AND_ADD_4:
+ case BUILT_IN_FETCH_AND_ADD_8:
+ target = expand_builtin_sync_operation (arglist, PLUS,
+ false, target, ignore);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FETCH_AND_SUB_1:
+ case BUILT_IN_FETCH_AND_SUB_2:
+ case BUILT_IN_FETCH_AND_SUB_4:
+ case BUILT_IN_FETCH_AND_SUB_8:
+ target = expand_builtin_sync_operation (arglist, MINUS,
+ false, target, ignore);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FETCH_AND_OR_1:
+ case BUILT_IN_FETCH_AND_OR_2:
+ case BUILT_IN_FETCH_AND_OR_4:
+ case BUILT_IN_FETCH_AND_OR_8:
+ target = expand_builtin_sync_operation (arglist, IOR,
+ false, target, ignore);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FETCH_AND_AND_1:
+ case BUILT_IN_FETCH_AND_AND_2:
+ case BUILT_IN_FETCH_AND_AND_4:
+ case BUILT_IN_FETCH_AND_AND_8:
+ target = expand_builtin_sync_operation (arglist, AND,
+ false, target, ignore);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FETCH_AND_XOR_1:
+ case BUILT_IN_FETCH_AND_XOR_2:
+ case BUILT_IN_FETCH_AND_XOR_4:
+ case BUILT_IN_FETCH_AND_XOR_8:
+ target = expand_builtin_sync_operation (arglist, XOR,
+ false, target, ignore);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FETCH_AND_NAND_1:
+ case BUILT_IN_FETCH_AND_NAND_2:
+ case BUILT_IN_FETCH_AND_NAND_4:
+ case BUILT_IN_FETCH_AND_NAND_8:
+ target = expand_builtin_sync_operation (arglist, NOT,
+ false, target, ignore);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_ADD_AND_FETCH_1:
+ case BUILT_IN_ADD_AND_FETCH_2:
+ case BUILT_IN_ADD_AND_FETCH_4:
+ case BUILT_IN_ADD_AND_FETCH_8:
+ target = expand_builtin_sync_operation (arglist, PLUS,
+ true, target, ignore);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_SUB_AND_FETCH_1:
+ case BUILT_IN_SUB_AND_FETCH_2:
+ case BUILT_IN_SUB_AND_FETCH_4:
+ case BUILT_IN_SUB_AND_FETCH_8:
+ target = expand_builtin_sync_operation (arglist, MINUS,
+ true, target, ignore);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_OR_AND_FETCH_1:
+ case BUILT_IN_OR_AND_FETCH_2:
+ case BUILT_IN_OR_AND_FETCH_4:
+ case BUILT_IN_OR_AND_FETCH_8:
+ target = expand_builtin_sync_operation (arglist, IOR,
+ true, target, ignore);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_AND_AND_FETCH_1:
+ case BUILT_IN_AND_AND_FETCH_2:
+ case BUILT_IN_AND_AND_FETCH_4:
+ case BUILT_IN_AND_AND_FETCH_8:
+ target = expand_builtin_sync_operation (arglist, AND,
+ true, target, ignore);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_XOR_AND_FETCH_1:
+ case BUILT_IN_XOR_AND_FETCH_2:
+ case BUILT_IN_XOR_AND_FETCH_4:
+ case BUILT_IN_XOR_AND_FETCH_8:
+ target = expand_builtin_sync_operation (arglist, XOR,
+ true, target, ignore);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_NAND_AND_FETCH_1:
+ case BUILT_IN_NAND_AND_FETCH_2:
+ case BUILT_IN_NAND_AND_FETCH_4:
+ case BUILT_IN_NAND_AND_FETCH_8:
+ target = expand_builtin_sync_operation (arglist, NOT,
+ true, target, ignore);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_BOOL_COMPARE_AND_SWAP_1:
+ case BUILT_IN_BOOL_COMPARE_AND_SWAP_2:
+ case BUILT_IN_BOOL_COMPARE_AND_SWAP_4:
+ case BUILT_IN_BOOL_COMPARE_AND_SWAP_8:
+ if (mode == VOIDmode)
+ mode = TYPE_MODE (boolean_type_node);
+ if (!target || !register_operand (target, mode))
+ target = gen_reg_rtx (mode);
+ target = expand_builtin_compare_and_swap (arglist, true, target);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_VAL_COMPARE_AND_SWAP_1:
+ case BUILT_IN_VAL_COMPARE_AND_SWAP_2:
+ case BUILT_IN_VAL_COMPARE_AND_SWAP_4:
+ case BUILT_IN_VAL_COMPARE_AND_SWAP_8:
+ target = expand_builtin_compare_and_swap (arglist, false, target);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_LOCK_TEST_AND_SET_1:
+ case BUILT_IN_LOCK_TEST_AND_SET_2:
+ case BUILT_IN_LOCK_TEST_AND_SET_4:
+ case BUILT_IN_LOCK_TEST_AND_SET_8:
+ target = expand_builtin_lock_test_and_set (arglist, target);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_LOCK_RELEASE_1:
+ case BUILT_IN_LOCK_RELEASE_2:
+ case BUILT_IN_LOCK_RELEASE_4:
+ case BUILT_IN_LOCK_RELEASE_8:
+ expand_builtin_lock_release (arglist);
+ return const0_rtx;
+
+ case BUILT_IN_SYNCHRONIZE:
+ expand_builtin_synchronize ();
+ return const0_rtx;
+
default: /* just do library call, if unknown builtin */
break;
}
STRIP_NOPS (arglist);
/* If we know this is a constant, emit the constant of one. */
- if (TREE_CODE_CLASS (TREE_CODE (arglist)) == 'c'
+ if (CONSTANT_CLASS_P (arglist)
|| (TREE_CODE (arglist) == CONSTRUCTOR
- && TREE_CONSTANT (arglist))
- || (TREE_CODE (arglist) == ADDR_EXPR
- && TREE_CODE (TREE_OPERAND (arglist, 0)) == STRING_CST))
+ && TREE_CONSTANT (arglist)))
return integer_one_node;
+ if (TREE_CODE (arglist) == ADDR_EXPR)
+ {
+ tree op = TREE_OPERAND (arglist, 0);
+ if (TREE_CODE (op) == STRING_CST
+ || (TREE_CODE (op) == ARRAY_REF
+ && integer_zerop (TREE_OPERAND (op, 1))
+ && TREE_CODE (TREE_OPERAND (op, 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
{
REAL_VALUE_TYPE real;
+ /* __builtin_inff is intended to be usable to define INFINITY on all
+ targets. If an infinity is not available, INFINITY expands "to a
+ positive constant of type float that overflows at translation
+ time", footnote "In this case, using INFINITY will violate the
+ constraint in 6.4.4 and thus require a diagnostic." (C99 7.12#4).
+ Thus we pedwarn to ensure this constraint violation is
+ diagnosed. */
if (!MODE_HAS_INFINITIES (TYPE_MODE (type)) && warn)
- warning ("target format does not support infinity");
+ pedwarn ("target format does not support infinity");
real_inf (&real);
return build_real (type, real);
Do the transformation. */
static tree
-fold_trunc_transparent_mathfn (tree exp)
+fold_trunc_transparent_mathfn (tree fndecl, tree arglist)
{
- tree fndecl = get_callee_fndecl (exp);
- tree arglist = TREE_OPERAND (exp, 1);
enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
tree arg;
if (optimize)
{
tree arg0 = strip_float_extensions (arg);
- tree ftype = TREE_TYPE (exp);
+ tree ftype = TREE_TYPE (TREE_TYPE (fndecl));
tree newtype = TREE_TYPE (arg0);
tree decl;
the argument, for instance lround((double)f) -> lroundf (f). */
static tree
-fold_fixed_mathfn (tree exp)
+fold_fixed_mathfn (tree fndecl, tree arglist)
{
- tree fndecl = get_callee_fndecl (exp);
- tree arglist = TREE_OPERAND (exp, 1);
enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
tree 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 fold (build1 (FIX_TRUNC_EXPR, TREE_TYPE (exp), arg));
+ return fold (build1 (FIX_TRUNC_EXPR, TREE_TYPE (TREE_TYPE (fndecl)), arg));
if (optimize)
{
}
}
- /* Optimize sqrt(pow(x,y)) = pow(x,y*0.5). */
+ /* Optimize sqrt(pow(x,y)) = pow(|x|,y*0.5). */
if (flag_unsafe_math_optimizations
&& (fcode == BUILT_IN_POW
|| fcode == BUILT_IN_POWF
tree powfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
tree arg1 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1)));
- tree narg1 = fold (build2 (MULT_EXPR, type, arg1,
- build_real (type, dconsthalf)));
+ tree narg1;
+ if (!tree_expr_nonnegative_p (arg0))
+ arg0 = build1 (ABS_EXPR, type, arg0);
+ narg1 = fold (build2 (MULT_EXPR, type, arg1,
+ build_real (type, dconsthalf)));
arglist = tree_cons (NULL_TREE, arg0,
build_tree_list (NULL_TREE, narg1));
return build_function_call_expr (powfn, arglist);
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 (build2 (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))
+ if (flag_unsafe_math_optimizations)
{
- tree powfn = mathfn_built_in (type, BUILT_IN_POW);
+ /* Optimize cbrt(expN(x)) -> expN(x/3). */
+ if (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 (build2 (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);
+ }
- if (powfn)
- {
- tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
- tree tree_root;
- REAL_VALUE_TYPE dconstroot = dconstthird;
+ /* Optimize cbrt(sqrt(x)) -> pow(x,1/6). */
+ if (BUILTIN_SQRT_P (fcode))
+ {
+ tree powfn = mathfn_built_in (type, BUILT_IN_POW);
- 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);
+ 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);
+ }
}
+ /* Optimize cbrt(cbrt(x)) -> pow(x,1/9) iff x is nonnegative. */
+ if (BUILTIN_CBRT_P (fcode))
+ {
+ tree arg0 = TREE_VALUE (TREE_OPERAND (arg, 1));
+ if (tree_expr_nonnegative_p (arg0))
+ {
+ tree powfn = mathfn_built_in (type, BUILT_IN_POW);
+
+ if (powfn)
+ {
+ tree tree_root;
+ REAL_VALUE_TYPE dconstroot;
+
+ real_arithmetic (&dconstroot, MULT_EXPR, &dconstthird, &dconstthird);
+ 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 cbrt(pow(x,y)) -> pow(x,y/3) iff x is nonnegative. */
+ if (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF
+ || fcode == BUILT_IN_POWL)
+ {
+ tree arg00 = TREE_VALUE (TREE_OPERAND (arg, 1));
+ tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1)));
+ if (tree_expr_nonnegative_p (arg00))
+ {
+ tree powfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
+ const REAL_VALUE_TYPE dconstroot
+ = real_value_truncate (TYPE_MODE (type), dconstthird);
+ tree narg01 = fold (build2 (MULT_EXPR, type, arg01,
+ build_real (type, dconstroot)));
+ arglist = tree_cons (NULL_TREE, arg00,
+ build_tree_list (NULL_TREE, narg01));
+ return build_function_call_expr (powfn, arglist);
+ }
+ }
}
return NULL_TREE;
}
NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_trunc (tree exp)
+fold_builtin_trunc (tree fndecl, tree arglist)
{
- tree arglist = TREE_OPERAND (exp, 1);
tree arg;
if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
{
REAL_VALUE_TYPE r, x;
- tree type = TREE_TYPE (exp);
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
x = TREE_REAL_CST (arg);
real_trunc (&r, TYPE_MODE (type), &x);
return build_real (type, r);
}
- return fold_trunc_transparent_mathfn (exp);
+ return fold_trunc_transparent_mathfn (fndecl, arglist);
}
/* 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)
+fold_builtin_floor (tree fndecl, tree arglist)
{
- tree arglist = TREE_OPERAND (exp, 1);
tree arg;
if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
x = TREE_REAL_CST (arg);
if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
{
- tree type = TREE_TYPE (exp);
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
REAL_VALUE_TYPE r;
real_floor (&r, TYPE_MODE (type), &x);
}
}
- return fold_trunc_transparent_mathfn (exp);
+ return fold_trunc_transparent_mathfn (fndecl, arglist);
}
/* 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)
+fold_builtin_ceil (tree fndecl, tree arglist)
{
- tree arglist = TREE_OPERAND (exp, 1);
tree arg;
if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
x = TREE_REAL_CST (arg);
if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
{
- tree type = TREE_TYPE (exp);
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
REAL_VALUE_TYPE r;
real_ceil (&r, TYPE_MODE (type), &x);
}
}
- return fold_trunc_transparent_mathfn (exp);
+ return fold_trunc_transparent_mathfn (fndecl, arglist);
}
/* 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)
+fold_builtin_round (tree fndecl, tree arglist)
{
- tree arglist = TREE_OPERAND (exp, 1);
tree arg;
if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
x = TREE_REAL_CST (arg);
if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
{
- tree type = TREE_TYPE (exp);
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
REAL_VALUE_TYPE r;
real_round (&r, TYPE_MODE (type), &x);
}
}
- return fold_trunc_transparent_mathfn (exp);
+ return fold_trunc_transparent_mathfn (fndecl, arglist);
}
/* Fold function call to builtin lround, lroundf or lroundl (or the
- corresponding long long versions). Return NULL_TREE if no
- simplification can be made. */
+ corresponding long long versions) and other rounding functions.
+ Return NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_lround (tree exp)
+fold_builtin_int_roundingfn (tree fndecl, tree arglist)
{
- tree arglist = TREE_OPERAND (exp, 1);
tree arg;
if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
if (! REAL_VALUE_ISNAN (x) && ! REAL_VALUE_ISINF (x))
{
- tree itype = TREE_TYPE (exp), ftype = TREE_TYPE (arg), result;
+ tree itype = TREE_TYPE (TREE_TYPE (fndecl));
+ tree ftype = TREE_TYPE (arg), result;
HOST_WIDE_INT hi, lo;
REAL_VALUE_TYPE r;
- real_round (&r, TYPE_MODE (ftype), &x);
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_LFLOOR:
+ case BUILT_IN_LFLOORF:
+ case BUILT_IN_LFLOORL:
+ case BUILT_IN_LLFLOOR:
+ case BUILT_IN_LLFLOORF:
+ case BUILT_IN_LLFLOORL:
+ real_floor (&r, TYPE_MODE (ftype), &x);
+ break;
+
+ case BUILT_IN_LCEIL:
+ case BUILT_IN_LCEILF:
+ case BUILT_IN_LCEILL:
+ case BUILT_IN_LLCEIL:
+ case BUILT_IN_LLCEILF:
+ case BUILT_IN_LLCEILL:
+ real_ceil (&r, TYPE_MODE (ftype), &x);
+ break;
+
+ case BUILT_IN_LROUND:
+ case BUILT_IN_LROUNDF:
+ case BUILT_IN_LROUNDL:
+ case BUILT_IN_LLROUND:
+ case BUILT_IN_LLROUNDF:
+ case BUILT_IN_LLROUNDL:
+ real_round (&r, TYPE_MODE (ftype), &x);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
REAL_VALUE_TO_INT (&lo, &hi, r);
result = build_int_cst_wide (NULL_TREE, lo, hi);
if (int_fits_type_p (result, itype))
}
}
- return fold_fixed_mathfn (exp);
+ return fold_fixed_mathfn (fndecl, arglist);
}
/* Fold function call to builtin ffs, clz, ctz, popcount and parity
Return NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_bitop (tree exp)
+fold_builtin_bitop (tree fndecl, tree arglist)
{
- tree fndecl = get_callee_fndecl (exp);
- tree arglist = TREE_OPERAND (exp, 1);
tree arg;
if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
gcc_unreachable ();
}
- return build_int_cst (TREE_TYPE (exp), result);
+ return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), result);
}
return NULL_TREE;
function. VALUE is the base of the logN function. */
static tree
-fold_builtin_logarithm (tree exp, const REAL_VALUE_TYPE *value)
+fold_builtin_logarithm (tree fndecl, tree arglist,
+ const REAL_VALUE_TYPE *value)
{
- tree arglist = TREE_OPERAND (exp, 1);
-
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);
static tree
fold_builtin_pow (tree fndecl, tree arglist, tree type)
{
- enum built_in_function fcode;
tree arg0 = TREE_VALUE (arglist);
tree 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);
/* Optimize pow(x,0.0) = 1.0. */
}
}
- /* Attempt to evaluate pow at compile-time. */
- if (TREE_CODE (arg0) == REAL_CST
- && ! TREE_CONSTANT_OVERFLOW (arg0))
+ /* Check for an integer exponent. */
+ n = real_to_integer (&c);
+ real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
+ if (real_identical (&c, &cint))
{
- REAL_VALUE_TYPE cint;
- HOST_WIDE_INT n;
-
- n = real_to_integer (&c);
- real_from_integer (&cint, VOIDmode, n,
- n < 0 ? -1 : 0, 0);
- if (real_identical (&c, &cint))
+ /* Attempt to evaluate pow at compile-time. */
+ if (TREE_CODE (arg0) == REAL_CST
+ && ! TREE_CONSTANT_OVERFLOW (arg0))
{
REAL_VALUE_TYPE x;
bool inexact;
if (flag_unsafe_math_optimizations || !inexact)
return build_real (type, x);
}
+
+ /* Strip sign ops from even integer powers. */
+ if ((n & 1) == 0 && flag_unsafe_math_optimizations)
+ {
+ tree narg0 = fold_strip_sign_ops (arg0);
+ if (narg0)
+ {
+ arglist = build_tree_list (NULL_TREE, arg1);
+ arglist = tree_cons (NULL_TREE, narg0, arglist);
+ return build_function_call_expr (fndecl, arglist);
+ }
+ }
}
}
- /* Optimize pow(expN(x),y) = expN(x*y). */
- fcode = builtin_mathfn_code (arg0);
- if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode))
+ if (flag_unsafe_math_optimizations)
{
- tree expfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0);
- tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1));
- arg = fold (build2 (MULT_EXPR, type, arg, arg1));
- arglist = build_tree_list (NULL_TREE, arg);
- return build_function_call_expr (expfn, arglist);
- }
+ const enum built_in_function fcode = builtin_mathfn_code (arg0);
- /* Optimize pow(sqrt(x),y) = pow(x,y*0.5). */
- if (flag_unsafe_math_optimizations && BUILTIN_SQRT_P (fcode))
- {
- tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1));
- tree narg1 = fold (build2 (MULT_EXPR, type, arg1,
- build_real (type, dconsthalf)));
+ /* Optimize pow(expN(x),y) = expN(x*y). */
+ if (BUILTIN_EXPONENT_P (fcode))
+ {
+ tree expfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0);
+ tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1));
+ arg = fold (build2 (MULT_EXPR, type, arg, arg1));
+ arglist = build_tree_list (NULL_TREE, arg);
+ return build_function_call_expr (expfn, arglist);
+ }
- arglist = tree_cons (NULL_TREE, narg0,
- build_tree_list (NULL_TREE, narg1));
- return build_function_call_expr (fndecl, arglist);
+ /* Optimize pow(sqrt(x),y) = pow(x,y*0.5). */
+ if (BUILTIN_SQRT_P (fcode))
+ {
+ tree narg0 = TREE_VALUE (TREE_OPERAND (arg0, 1));
+ tree narg1 = fold (build2 (MULT_EXPR, type, arg1,
+ build_real (type, dconsthalf)));
+
+ arglist = tree_cons (NULL_TREE, narg0,
+ build_tree_list (NULL_TREE, narg1));
+ return build_function_call_expr (fndecl, arglist);
+ }
+
+ /* Optimize pow(cbrt(x),y) = pow(x,y/3) iff x is nonnegative. */
+ if (BUILTIN_CBRT_P (fcode))
+ {
+ tree arg = TREE_VALUE (TREE_OPERAND (arg0, 1));
+ if (tree_expr_nonnegative_p (arg))
+ {
+ const REAL_VALUE_TYPE dconstroot
+ = real_value_truncate (TYPE_MODE (type), dconstthird);
+ tree narg1 = fold (build2 (MULT_EXPR, type, arg1,
+ build_real (type, dconstroot)));
+ arglist = tree_cons (NULL_TREE, arg,
+ build_tree_list (NULL_TREE, narg1));
+ return build_function_call_expr (fndecl, arglist);
+ }
+ }
+
+ /* Optimize pow(pow(x,y),z) = pow(x,y*z). */
+ if (fcode == BUILT_IN_POW || fcode == BUILT_IN_POWF
+ || fcode == BUILT_IN_POWL)
+ {
+ tree arg00 = TREE_VALUE (TREE_OPERAND (arg0, 1));
+ tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg0, 1)));
+ tree narg1 = fold (build2 (MULT_EXPR, type, arg01, arg1));
+ arglist = tree_cons (NULL_TREE, arg00,
+ build_tree_list (NULL_TREE, narg1));
+ return build_function_call_expr (fndecl, arglist);
+ }
}
- /* Optimize pow(pow(x,y),z) = pow(x,y*z). */
- if (flag_unsafe_math_optimizations
- && (fcode == BUILT_IN_POW
- || fcode == BUILT_IN_POWF
- || fcode == BUILT_IN_POWL))
+ return NULL_TREE;
+}
+
+/* Fold a builtin function call to powi, powif, or powil. Return
+ NULL_TREE if no simplification can be made. */
+static tree
+fold_builtin_powi (tree fndecl ATTRIBUTE_UNUSED, tree arglist, tree type)
+{
+ tree arg0 = TREE_VALUE (arglist);
+ tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+
+ if (!validate_arglist (arglist, REAL_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return NULL_TREE;
+
+ /* Optimize pow(1.0,y) = 1.0. */
+ if (real_onep (arg0))
+ return omit_one_operand (type, build_real (type, dconst1), arg1);
+
+ if (host_integerp (arg1, 0))
{
- tree arg00 = TREE_VALUE (TREE_OPERAND (arg0, 1));
- tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg0, 1)));
- tree narg1 = fold (build2 (MULT_EXPR, type, arg01, arg1));
- arglist = tree_cons (NULL_TREE, arg00,
- build_tree_list (NULL_TREE, narg1));
- return build_function_call_expr (fndecl, arglist);
+ HOST_WIDE_INT c = TREE_INT_CST_LOW (arg1);
+
+ /* Evaluate powi at compile-time. */
+ if (TREE_CODE (arg0) == REAL_CST
+ && ! TREE_CONSTANT_OVERFLOW (arg0))
+ {
+ REAL_VALUE_TYPE x;
+ x = TREE_REAL_CST (arg0);
+ real_powi (&x, TYPE_MODE (type), &x, c);
+ return build_real (type, x);
+ }
+
+ /* Optimize pow(x,0) = 1.0. */
+ if (c == 0)
+ return omit_one_operand (type, build_real (type, dconst1),
+ arg0);
+
+ /* Optimize pow(x,1) = x. */
+ if (c == 1)
+ return arg0;
+
+ /* Optimize pow(x,-1) = 1.0/x. */
+ if (c == -1)
+ return fold (build2 (RDIV_EXPR, type,
+ build_real (type, dconst1), arg0));
}
+
return NULL_TREE;
}
VALUE is the value which will be raised to a power. */
static tree
-fold_builtin_exponent (tree exp, const REAL_VALUE_TYPE *value)
+fold_builtin_exponent (tree fndecl, tree arglist,
+ const REAL_VALUE_TYPE *value)
{
- tree arglist = TREE_OPERAND (exp, 1);
-
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);
NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_memcpy (tree exp)
+fold_builtin_memcpy (tree fndecl, tree arglist)
{
- tree arglist = TREE_OPERAND (exp, 1);
tree dest, src, len;
if (!validate_arglist (arglist,
/* If the LEN parameter is zero, return DEST. */
if (integer_zerop (len))
- return omit_one_operand (TREE_TYPE (exp), dest, src);
+ return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), 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 omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len);
return 0;
}
NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_mempcpy (tree exp)
+fold_builtin_mempcpy (tree arglist, tree type, int endp)
{
- tree arglist = TREE_OPERAND (exp, 1);
- tree dest, src, len;
-
- if (!validate_arglist (arglist,
- POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
- return 0;
+ if (validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ {
+ tree dest = TREE_VALUE (arglist);
+ tree src = TREE_VALUE (TREE_CHAIN (arglist));
+ tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
- 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 (type, dest, src);
- /* 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+LEN. */
+ if (operand_equal_p (src, dest, 0))
+ {
+ if (endp == 0)
+ return omit_one_operand (type, dest, len);
- /* If SRC and DEST are the same (and not volatile), return DEST+LEN. */
- if (operand_equal_p (src, dest, 0))
- {
- tree temp = fold_convert (TREE_TYPE (dest), len);
- temp = fold (build2 (PLUS_EXPR, TREE_TYPE (dest), dest, temp));
- return fold_convert (TREE_TYPE (exp), temp);
+ if (endp == 2)
+ len = fold (build2 (MINUS_EXPR, TREE_TYPE (len), len,
+ ssize_int (1)));
+
+ len = fold_convert (TREE_TYPE (dest), len);
+ len = fold (build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len));
+ return fold_convert (type, len);
+ }
}
-
return 0;
}
NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_memmove (tree exp)
+fold_builtin_memmove (tree arglist, tree type)
{
- tree arglist = TREE_OPERAND (exp, 1);
tree dest, src, len;
if (!validate_arglist (arglist,
/* If the LEN parameter is zero, return DEST. */
if (integer_zerop (len))
- return omit_one_operand (TREE_TYPE (exp), dest, src);
+ return omit_one_operand (type, 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 omit_one_operand (type, dest, len);
return 0;
}
simplification can be made. */
tree
-fold_builtin_strcpy (tree exp, tree len)
+fold_builtin_strcpy (tree fndecl, tree arglist, tree len)
{
- tree arglist = TREE_OPERAND (exp, 1);
tree dest, src, fn;
if (!validate_arglist (arglist,
/* If SRC and DEST are the same (and not volatile), return DEST. */
if (operand_equal_p (src, dest, 0))
- return fold_convert (TREE_TYPE (exp), dest);
+ return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), dest);
if (optimize_size)
return 0;
arglist = build_tree_list (NULL_TREE, len);
arglist = tree_cons (NULL_TREE, src, arglist);
arglist = tree_cons (NULL_TREE, dest, arglist);
- return fold_convert (TREE_TYPE (exp),
+ return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)),
build_function_call_expr (fn, arglist));
}
can be made. */
tree
-fold_builtin_strncpy (tree exp, tree slen)
+fold_builtin_strncpy (tree fndecl, tree arglist, tree slen)
{
- tree arglist = TREE_OPERAND (exp, 1);
tree dest, src, len, fn;
if (!validate_arglist (arglist,
/* If the LEN parameter is zero, return DEST. */
if (integer_zerop (len))
- return omit_one_operand (TREE_TYPE (exp), dest, src);
+ return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src);
/* We can't compare slen with len as constants below if len is not a
constant. */
fn = implicit_built_in_decls[BUILT_IN_MEMCPY];
if (!fn)
return 0;
- return fold_convert (TREE_TYPE (exp),
+ return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)),
build_function_call_expr (fn, arglist));
}
-/* Fold function call to builtin strchr and strrchr.
- Return NULL_TREE if no simplification can be made. */
-
-static tree
-fold_builtin_strchr (tree exp, bool actually_strrchr)
-{
- tree arglist = TREE_OPERAND (exp, 1);
- if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
- return 0;
- else
- {
- tree s1 = TREE_VALUE (arglist), s2 = TREE_VALUE (TREE_CHAIN (arglist));
- const char *p1;
-
- if (TREE_CODE (s2) != INTEGER_CST)
- return 0;
-
- p1 = c_getstr (s1);
- if (p1 != NULL)
- {
- char c;
- const char *r;
-
- if (target_char_cast (s2, &c))
- return 0;
-
- r = actually_strrchr ? strrchr (p1, c) : strchr (p1, c);
-
- if (r == NULL)
- return build_int_cst (TREE_TYPE (s1), 0);
-
- /* Return an offset into the constant string argument. */
- return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
- s1, build_int_cst (TREE_TYPE (s1), r - p1)));
- }
-
- if (actually_strrchr)
- {
- tree fn;
-
- if (!integer_zerop (s2))
- return 0;
-
- fn = implicit_built_in_decls[BUILT_IN_STRCHR];
- if (!fn)
- return 0;
-
- /* Transform strrchr(s1, '\0') to strchr(s1, '\0'). */
- return build_function_call_expr (fn, arglist);
- }
-
- return 0;
- }
-}
-
/* Fold function call to builtin memcmp. Return
NULL_TREE if no simplification can be made. */
NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_signbit (tree exp)
+fold_builtin_signbit (tree fndecl, tree arglist)
{
- tree arglist = TREE_OPERAND (exp, 1);
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
tree arg, temp;
if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
c = TREE_REAL_CST (arg);
temp = REAL_VALUE_NEGATIVE (c) ? integer_one_node : integer_zero_node;
- return fold_convert (TREE_TYPE (exp), temp);
+ return fold_convert (type, temp);
}
/* 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);
+ return omit_one_operand (type, integer_zero_node, arg);
/* If ARG's format doesn't have signed zeros, return "arg < 0.0". */
if (!HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (arg))))
- return fold (build2 (LT_EXPR, TREE_TYPE (exp), arg,
+ return fold (build2 (LT_EXPR, type, arg,
build_real (TREE_TYPE (arg), dconst0)));
return NULL_TREE;
Return NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_copysign (tree arglist, tree type)
+fold_builtin_copysign (tree fndecl, tree arglist, tree type)
{
- tree arg1, arg2;
+ tree arg1, arg2, tem;
if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
return NULL_TREE;
fold (build1 (ABS_EXPR, type, arg1)),
arg2);
+ /* Strip sign changing operations for the first argument. */
+ tem = fold_strip_sign_ops (arg1);
+ if (tem)
+ {
+ arglist = tree_cons (NULL_TREE, tem, TREE_CHAIN (arglist));
+ return build_function_call_expr (fndecl, arglist);
+ }
+
return NULL_TREE;
}
else
{
/* Transform isdigit(c) -> (unsigned)(c) - '0' <= 9. */
- /* According to the C standard, isdigit is unaffected by locale. */
- tree arg = TREE_VALUE (arglist);
- arg = fold_convert (unsigned_type_node, arg);
+ /* According to the C standard, isdigit is unaffected by locale.
+ However, it definitely is affected by the target character set. */
+ tree arg;
+ unsigned HOST_WIDE_INT target_digit0
+ = lang_hooks.to_target_charset ('0');
+
+ if (target_digit0 == 0)
+ return NULL_TREE;
+
+ arg = fold_convert (unsigned_type_node, TREE_VALUE (arglist));
arg = build2 (MINUS_EXPR, unsigned_type_node, arg,
- build_int_cst (unsigned_type_node, TARGET_DIGIT0));
+ build_int_cst (unsigned_type_node, target_digit0));
arg = build2 (LE_EXPR, integer_type_node, arg,
build_int_cst (unsigned_type_node, 9));
arg = fold (arg);
return 0;
arg = TREE_VALUE (arglist);
+ arg = fold_convert (type, arg);
if (TREE_CODE (arg) == REAL_CST)
return fold_abs_const (arg, type);
return fold (build1 (ABS_EXPR, type, arg));
return 0;
arg = TREE_VALUE (arglist);
+ arg = fold_convert (type, arg);
if (TREE_CODE (arg) == INTEGER_CST)
return fold_abs_const (arg, type);
return fold (build1 (ABS_EXPR, type, arg));
EXP is the CALL_EXPR for the call. */
static tree
-fold_builtin_classify (tree exp, int builtin_index)
+fold_builtin_classify (tree fndecl, tree arglist, int builtin_index)
{
- tree fndecl = get_callee_fndecl (exp);
- tree arglist = TREE_OPERAND (exp, 1);
tree type = TREE_TYPE (TREE_TYPE (fndecl));
tree arg;
REAL_VALUE_TYPE r;
/* Check that we have exactly one argument. */
if (arglist == 0)
{
- error ("too few arguments to function `%s'",
+ error ("too few arguments to function %qs",
IDENTIFIER_POINTER (DECL_NAME (fndecl)));
return error_mark_node;
}
else if (TREE_CHAIN (arglist) != 0)
{
- error ("too many arguments to function `%s'",
+ error ("too many arguments to function %qs",
IDENTIFIER_POINTER (DECL_NAME (fndecl)));
return error_mark_node;
}
else
{
- error ("non-floating-point argument to function `%s'",
+ error ("non-floating-point argument to function %qs",
IDENTIFIER_POINTER (DECL_NAME (fndecl)));
return error_mark_node;
}
}
/* Fold a call to an unordered comparison function such as
- __builtin_isgreater(). EXP is the CALL_EXPR for the call.
+ __builtin_isgreater(). FNDECL is the FUNCTION_DECL for the function
+ being called and ARGLIST is the argument list for the call.
UNORDERED_CODE and ORDERED_CODE are comparison codes that give
the opposite of the desired result. UNORDERED_CODE is used
for modes that can hold NaNs and ORDERED_CODE is used for
the rest. */
static tree
-fold_builtin_unordered_cmp (tree exp,
+fold_builtin_unordered_cmp (tree fndecl, tree arglist,
enum tree_code unordered_code,
enum tree_code ordered_code)
{
- tree fndecl = get_callee_fndecl (exp);
- tree arglist = TREE_OPERAND (exp, 1);
tree type = TREE_TYPE (TREE_TYPE (fndecl));
enum tree_code code;
tree arg0, arg1;
+ tree type0, type1;
+ enum tree_code code0, code1;
+ tree cmp_type = NULL_TREE;
if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
{
- enum tree_code code0, code1;
- tree type0, type1;
- tree cmp_type = 0;
-
/* Check that we have exactly two arguments. */
if (arglist == 0 || TREE_CHAIN (arglist) == 0)
{
- error ("too few arguments to function `%s'",
+ error ("too few arguments to function %qs",
IDENTIFIER_POINTER (DECL_NAME (fndecl)));
return error_mark_node;
}
else if (TREE_CHAIN (TREE_CHAIN (arglist)) != 0)
{
- error ("too many arguments to function `%s'",
+ error ("too many arguments to function %qs",
IDENTIFIER_POINTER (DECL_NAME (fndecl)));
return error_mark_node;
}
-
- arg0 = TREE_VALUE (arglist);
- arg1 = TREE_VALUE (TREE_CHAIN (arglist));
-
- type0 = TREE_TYPE (arg0);
- type1 = TREE_TYPE (arg1);
-
- code0 = TREE_CODE (type0);
- code1 = TREE_CODE (type1);
-
- if (code0 == REAL_TYPE && code1 == REAL_TYPE)
- /* Choose the wider of two real types. */
- cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1)
- ? type0 : type1;
- else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE)
- cmp_type = type0;
- else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE)
- cmp_type = type1;
- else
- {
- error ("non-floating-point argument to function `%s'",
- IDENTIFIER_POINTER (DECL_NAME (fndecl)));
- return error_mark_node;
- }
-
- arg0 = fold_convert (cmp_type, arg0);
- arg1 = fold_convert (cmp_type, arg1);
}
+
+ arg0 = TREE_VALUE (arglist);
+ arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+
+ type0 = TREE_TYPE (arg0);
+ type1 = TREE_TYPE (arg1);
+
+ code0 = TREE_CODE (type0);
+ code1 = TREE_CODE (type1);
+
+ if (code0 == REAL_TYPE && code1 == REAL_TYPE)
+ /* Choose the wider of two real types. */
+ cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1)
+ ? type0 : type1;
+ else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE)
+ cmp_type = type0;
+ else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE)
+ cmp_type = type1;
else
{
- arg0 = TREE_VALUE (arglist);
- arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+ error ("non-floating-point argument to function %qs",
+ IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+ return error_mark_node;
}
+
+ arg0 = fold_convert (cmp_type, arg0);
+ arg1 = fold_convert (cmp_type, arg1);
if (unordered_code == UNORDERED_EXPR)
{
fold (build2 (code, type, arg0, arg1))));
}
+/* Fold a call to one of the external complex multiply libcalls. */
+
+static tree
+fold_builtin_complex_mul (tree type, tree arglist)
+{
+ tree ar, ai, br, bi;
+
+ if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, REAL_TYPE,
+ REAL_TYPE, VOID_TYPE))
+ return NULL;
+
+ ar = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+ ai = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+ br = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+ bi = TREE_VALUE (arglist);
+
+ return fold_complex_mult_parts (type, ar, ai, br, bi);
+}
+
+/* Fold a call to one of the external complex division libcalls. */
+
+static tree
+fold_builtin_complex_div (tree type, tree arglist)
+{
+ tree ar, ai, br, bi;
+
+ if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, REAL_TYPE,
+ REAL_TYPE, VOID_TYPE))
+ return NULL;
+
+ ar = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+ ai = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+ br = TREE_VALUE (arglist); arglist = TREE_CHAIN (arglist);
+ bi = TREE_VALUE (arglist);
+
+ return fold_complex_div_parts (type, ar, ai, br, bi, RDIV_EXPR);
+}
+
/* Used by constant folding to simplify calls to builtin functions. EXP is
the CALL_EXPR of a call to a builtin function. IGNORE is true if the
result of the function call is ignored. This function returns NULL_TREE
if no simplification was possible. */
static tree
-fold_builtin_1 (tree exp, bool ignore)
+fold_builtin_1 (tree fndecl, tree arglist, bool ignore)
{
- tree fndecl = get_callee_fndecl (exp);
- tree arglist = TREE_OPERAND (exp, 1);
tree type = TREE_TYPE (TREE_TYPE (fndecl));
+ enum built_in_function fcode;
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
- return 0;
+ return targetm.fold_builtin (fndecl, arglist, ignore);
- switch (DECL_FUNCTION_CODE (fndecl))
+ fcode = DECL_FUNCTION_CODE (fndecl);
+ switch (fcode)
{
+ case BUILT_IN_FPUTS:
+ return fold_builtin_fputs (arglist, ignore, false, NULL_TREE);
+
+ case BUILT_IN_FPUTS_UNLOCKED:
+ return fold_builtin_fputs (arglist, ignore, true, NULL_TREE);
+
+ case BUILT_IN_STRSTR:
+ return fold_builtin_strstr (arglist, type);
+
+ case BUILT_IN_STRCAT:
+ return fold_builtin_strcat (arglist);
+
+ case BUILT_IN_STRNCAT:
+ return fold_builtin_strncat (arglist);
+
+ case BUILT_IN_STRSPN:
+ return fold_builtin_strspn (arglist);
+
+ case BUILT_IN_STRCSPN:
+ return fold_builtin_strcspn (arglist);
+
+ case BUILT_IN_STRCHR:
+ case BUILT_IN_INDEX:
+ return fold_builtin_strchr (arglist, type);
+
+ case BUILT_IN_STRRCHR:
+ case BUILT_IN_RINDEX:
+ return fold_builtin_strrchr (arglist, type);
+
+ case BUILT_IN_STRCPY:
+ return fold_builtin_strcpy (fndecl, arglist, NULL_TREE);
+
+ case BUILT_IN_STRNCPY:
+ return fold_builtin_strncpy (fndecl, arglist, NULL_TREE);
+
+ case BUILT_IN_STRCMP:
+ return fold_builtin_strcmp (arglist);
+
+ case BUILT_IN_STRNCMP:
+ return fold_builtin_strncmp (arglist);
+
+ case BUILT_IN_STRPBRK:
+ return fold_builtin_strpbrk (arglist, type);
+
+ case BUILT_IN_BCMP:
+ case BUILT_IN_MEMCMP:
+ return fold_builtin_memcmp (arglist);
+
+ case BUILT_IN_SPRINTF:
+ return fold_builtin_sprintf (arglist, ignore);
+
case BUILT_IN_CONSTANT_P:
- return fold_builtin_constant_p (arglist);
+ {
+ tree val;
+
+ val = fold_builtin_constant_p (arglist);
+ /* Gimplification will pull the CALL_EXPR for the builtin out of
+ an if condition. When not optimizing, we'll not CSE it back.
+ To avoid link error types of regressions, return false now. */
+ if (!val && !optimize)
+ val = integer_zero_node;
+
+ return val;
+ }
case BUILT_IN_EXPECT:
return fold_builtin_expect (arglist);
case BUILT_IN_EXP:
case BUILT_IN_EXPF:
case BUILT_IN_EXPL:
- return fold_builtin_exponent (exp, &dconste);
+ return fold_builtin_exponent (fndecl, arglist, &dconste);
case BUILT_IN_EXP2:
case BUILT_IN_EXP2F:
case BUILT_IN_EXP2L:
- return fold_builtin_exponent (exp, &dconst2);
+ return fold_builtin_exponent (fndecl, arglist, &dconst2);
case BUILT_IN_EXP10:
case BUILT_IN_EXP10F:
case BUILT_IN_POW10:
case BUILT_IN_POW10F:
case BUILT_IN_POW10L:
- return fold_builtin_exponent (exp, &dconst10);
+ return fold_builtin_exponent (fndecl, arglist, &dconst10);
case BUILT_IN_LOG:
case BUILT_IN_LOGF:
case BUILT_IN_LOGL:
- return fold_builtin_logarithm (exp, &dconste);
+ return fold_builtin_logarithm (fndecl, arglist, &dconste);
case BUILT_IN_LOG2:
case BUILT_IN_LOG2F:
case BUILT_IN_LOG2L:
- return fold_builtin_logarithm (exp, &dconst2);
+ return fold_builtin_logarithm (fndecl, arglist, &dconst2);
case BUILT_IN_LOG10:
case BUILT_IN_LOG10F:
case BUILT_IN_LOG10L:
- return fold_builtin_logarithm (exp, &dconst10);
+ return fold_builtin_logarithm (fndecl, arglist, &dconst10);
case BUILT_IN_TAN:
case BUILT_IN_TANF:
case BUILT_IN_POWL:
return fold_builtin_pow (fndecl, arglist, type);
+ case BUILT_IN_POWI:
+ case BUILT_IN_POWIF:
+ case BUILT_IN_POWIL:
+ return fold_builtin_powi (fndecl, arglist, type);
+
case BUILT_IN_INF:
case BUILT_IN_INFF:
case BUILT_IN_INFL:
case BUILT_IN_FLOOR:
case BUILT_IN_FLOORF:
case BUILT_IN_FLOORL:
- return fold_builtin_floor (exp);
+ return fold_builtin_floor (fndecl, arglist);
case BUILT_IN_CEIL:
case BUILT_IN_CEILF:
case BUILT_IN_CEILL:
- return fold_builtin_ceil (exp);
+ return fold_builtin_ceil (fndecl, arglist);
case BUILT_IN_TRUNC:
case BUILT_IN_TRUNCF:
case BUILT_IN_TRUNCL:
- return fold_builtin_trunc (exp);
+ return fold_builtin_trunc (fndecl, arglist);
case BUILT_IN_ROUND:
case BUILT_IN_ROUNDF:
case BUILT_IN_ROUNDL:
- return fold_builtin_round (exp);
+ return fold_builtin_round (fndecl, arglist);
case BUILT_IN_NEARBYINT:
case BUILT_IN_NEARBYINTF:
case BUILT_IN_RINT:
case BUILT_IN_RINTF:
case BUILT_IN_RINTL:
- return fold_trunc_transparent_mathfn (exp);
-
+ return fold_trunc_transparent_mathfn (fndecl, arglist);
+
+ case BUILT_IN_LCEIL:
+ case BUILT_IN_LCEILF:
+ case BUILT_IN_LCEILL:
+ case BUILT_IN_LLCEIL:
+ case BUILT_IN_LLCEILF:
+ case BUILT_IN_LLCEILL:
+ case BUILT_IN_LFLOOR:
+ case BUILT_IN_LFLOORF:
+ case BUILT_IN_LFLOORL:
+ case BUILT_IN_LLFLOOR:
+ case BUILT_IN_LLFLOORF:
+ case BUILT_IN_LLFLOORL:
case BUILT_IN_LROUND:
case BUILT_IN_LROUNDF:
case BUILT_IN_LROUNDL:
case BUILT_IN_LLROUND:
case BUILT_IN_LLROUNDF:
case BUILT_IN_LLROUNDL:
- return fold_builtin_lround (exp);
+ return fold_builtin_int_roundingfn (fndecl, arglist);
case BUILT_IN_LRINT:
case BUILT_IN_LRINTF:
case BUILT_IN_LLRINT:
case BUILT_IN_LLRINTF:
case BUILT_IN_LLRINTL:
- return fold_fixed_mathfn (exp);
+ return fold_fixed_mathfn (fndecl, arglist);
case BUILT_IN_FFS:
case BUILT_IN_FFSL:
case BUILT_IN_PARITY:
case BUILT_IN_PARITYL:
case BUILT_IN_PARITYLL:
- return fold_builtin_bitop (exp);
+ return fold_builtin_bitop (fndecl, arglist);
case BUILT_IN_MEMCPY:
- return fold_builtin_memcpy (exp);
+ return fold_builtin_memcpy (fndecl, arglist);
case BUILT_IN_MEMPCPY:
- return fold_builtin_mempcpy (exp);
+ return fold_builtin_mempcpy (arglist, type, /*endp=*/1);
case BUILT_IN_MEMMOVE:
- return fold_builtin_memmove (exp);
-
- case BUILT_IN_STRCPY:
- return fold_builtin_strcpy (exp, NULL_TREE);
-
- case BUILT_IN_STRNCPY:
- return fold_builtin_strncpy (exp, NULL_TREE);
-
- case BUILT_IN_INDEX:
- case BUILT_IN_STRCHR:
- return fold_builtin_strchr (exp, false);
-
- case BUILT_IN_RINDEX:
- case BUILT_IN_STRRCHR:
- return fold_builtin_strchr (exp, true);
-
- case BUILT_IN_MEMCMP:
- return fold_builtin_memcmp (arglist);
-
- case BUILT_IN_STRCMP:
- return fold_builtin_strcmp (arglist);
-
- case BUILT_IN_STRNCMP:
- return fold_builtin_strncmp (arglist);
+ return fold_builtin_memmove (arglist, type);
case BUILT_IN_SIGNBIT:
case BUILT_IN_SIGNBITF:
case BUILT_IN_SIGNBITL:
- return fold_builtin_signbit (exp);
+ return fold_builtin_signbit (fndecl, arglist);
case BUILT_IN_ISASCII:
return fold_builtin_isascii (arglist);
case BUILT_IN_COPYSIGN:
case BUILT_IN_COPYSIGNF:
case BUILT_IN_COPYSIGNL:
- return fold_builtin_copysign (arglist, type);
+ return fold_builtin_copysign (fndecl, arglist, type);
case BUILT_IN_FINITE:
case BUILT_IN_FINITEF:
case BUILT_IN_FINITEL:
- return fold_builtin_classify (exp, BUILT_IN_FINITE);
+ return fold_builtin_classify (fndecl, arglist, BUILT_IN_FINITE);
case BUILT_IN_ISINF:
case BUILT_IN_ISINFF:
case BUILT_IN_ISINFL:
- return fold_builtin_classify (exp, BUILT_IN_ISINF);
+ return fold_builtin_classify (fndecl, arglist, BUILT_IN_ISINF);
case BUILT_IN_ISNAN:
case BUILT_IN_ISNANF:
case BUILT_IN_ISNANL:
- return fold_builtin_classify (exp, BUILT_IN_ISNAN);
+ return fold_builtin_classify (fndecl, arglist, BUILT_IN_ISNAN);
case BUILT_IN_ISGREATER:
- return fold_builtin_unordered_cmp (exp, UNLE_EXPR, LE_EXPR);
+ return fold_builtin_unordered_cmp (fndecl, arglist, UNLE_EXPR, LE_EXPR);
case BUILT_IN_ISGREATEREQUAL:
- return fold_builtin_unordered_cmp (exp, UNLT_EXPR, LT_EXPR);
+ return fold_builtin_unordered_cmp (fndecl, arglist, UNLT_EXPR, LT_EXPR);
case BUILT_IN_ISLESS:
- return fold_builtin_unordered_cmp (exp, UNGE_EXPR, GE_EXPR);
+ return fold_builtin_unordered_cmp (fndecl, arglist, UNGE_EXPR, GE_EXPR);
case BUILT_IN_ISLESSEQUAL:
- return fold_builtin_unordered_cmp (exp, UNGT_EXPR, GT_EXPR);
+ return fold_builtin_unordered_cmp (fndecl, arglist, UNGT_EXPR, GT_EXPR);
case BUILT_IN_ISLESSGREATER:
- return fold_builtin_unordered_cmp (exp, UNEQ_EXPR, EQ_EXPR);
+ return fold_builtin_unordered_cmp (fndecl, arglist, UNEQ_EXPR, EQ_EXPR);
case BUILT_IN_ISUNORDERED:
- return fold_builtin_unordered_cmp (exp, UNORDERED_EXPR, NOP_EXPR);
-
- case BUILT_IN_FPUTS:
- return fold_builtin_fputs (arglist, ignore, false, NULL_TREE);
+ return fold_builtin_unordered_cmp (fndecl, arglist, UNORDERED_EXPR,
+ NOP_EXPR);
- case BUILT_IN_FPUTS_UNLOCKED:
- return fold_builtin_fputs (arglist, ignore, true, NULL_TREE);
+ /* We do the folding for va_start in the expander. */
+ case BUILT_IN_VA_START:
+ break;
default:
+ if (fcode >= BUILT_IN_COMPLEX_MUL_MIN
+ && fcode <= BUILT_IN_COMPLEX_MUL_MAX)
+ return fold_builtin_complex_mul (type, arglist);
+ if (fcode >= BUILT_IN_COMPLEX_DIV_MIN
+ && fcode <= BUILT_IN_COMPLEX_DIV_MAX)
+ return fold_builtin_complex_div (type, arglist);
break;
}
call node earlier than the warning is generated. */
tree
-fold_builtin (tree exp, bool ignore)
+fold_builtin (tree fndecl, tree arglist, bool ignore)
{
- exp = fold_builtin_1 (exp, ignore);
+ tree exp = fold_builtin_1 (fndecl, arglist, ignore);
if (exp)
{
/* ??? Don't clobber shared nodes such as integer_zero_node. */
- if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c')
+ if (CONSTANT_CLASS_P (exp))
exp = build1 (NOP_EXPR, TREE_TYPE (exp), exp);
TREE_NO_WARNING (exp) = 1;
}
+
return exp;
}
/* If no parameters remain or the parameter's code does not
match the specified code, return false. Otherwise continue
checking any remaining arguments. */
- if (arglist == 0
- || code != TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))))
+ if (arglist == 0)
+ goto end;
+ if (code == POINTER_TYPE)
+ {
+ if (! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist))))
+ goto end;
+ }
+ else if (code != TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))))
goto end;
break;
}
return false;
}
-/* Front-end to the simplify_builtin_XXX routines.
-
- EXP is a call to a builtin function. If possible try to simplify
- that into a constant, expression or call to a more efficient
- builtin function.
-
- If IGNORE is nonzero, then the result of this builtin function
- call is ignored.
-
- If simplification is possible, return the simplified tree, otherwise
- return NULL_TREE. */
-
-tree
-simplify_builtin (tree exp, 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);
- tree val;
-
- switch (fcode)
- {
- case BUILT_IN_FPUTS:
- val = fold_builtin_fputs (arglist, ignore, false, NULL_TREE);
- break;
- case BUILT_IN_FPUTS_UNLOCKED:
- val = fold_builtin_fputs (arglist, ignore, true, NULL_TREE);
- break;
- case BUILT_IN_STRSTR:
- val = simplify_builtin_strstr (arglist);
- break;
- case BUILT_IN_STRCAT:
- val = simplify_builtin_strcat (arglist);
- break;
- case BUILT_IN_STRNCAT:
- val = simplify_builtin_strncat (arglist);
- break;
- case BUILT_IN_STRSPN:
- val = simplify_builtin_strspn (arglist);
- break;
- case BUILT_IN_STRCSPN:
- val = simplify_builtin_strcspn (arglist);
- break;
- case BUILT_IN_STRCHR:
- case BUILT_IN_INDEX:
- val = simplify_builtin_strchr (arglist);
- break;
- case BUILT_IN_STRRCHR:
- case BUILT_IN_RINDEX:
- val = simplify_builtin_strrchr (arglist);
- break;
- case BUILT_IN_STRCPY:
- val = fold_builtin_strcpy (exp, NULL_TREE);
- break;
- case BUILT_IN_STRNCPY:
- val = fold_builtin_strncpy (exp, NULL_TREE);
- break;
- case BUILT_IN_STRCMP:
- val = fold_builtin_strcmp (arglist);
- break;
- case BUILT_IN_STRNCMP:
- val = fold_builtin_strncmp (arglist);
- break;
- case BUILT_IN_STRPBRK:
- val = simplify_builtin_strpbrk (arglist);
- break;
- case BUILT_IN_BCMP:
- case BUILT_IN_MEMCMP:
- val = fold_builtin_memcmp (arglist);
- break;
- case BUILT_IN_VA_START:
- simplify_builtin_va_start (arglist);
- val = NULL_TREE;
- break;
- case BUILT_IN_SPRINTF:
- val = simplify_builtin_sprintf (arglist, ignore);
- break;
- case BUILT_IN_CONSTANT_P:
- val = fold_builtin_constant_p (arglist);
- /* Gimplification will pull the CALL_EXPR for the builtin out of
- an if condition. When not optimizing, we'll not CSE it back.
- To avoid link error types of regressions, return false now. */
- if (!val && !optimize)
- val = integer_zero_node;
- break;
- default:
- val = NULL_TREE;
- break;
- }
-
- if (val)
- val = fold_convert (TREE_TYPE (exp), val);
- return val;
-}
-
/* Simplify a call to the strstr builtin.
Return 0 if no simplification was possible, otherwise return the
form of the builtin function call. */
static tree
-simplify_builtin_strstr (tree arglist)
+fold_builtin_strstr (tree arglist, tree type)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
if (p1 != NULL)
{
const char *r = strstr (p1, p2);
+ tree tem;
if (r == NULL)
return build_int_cst (TREE_TYPE (s1), 0);
/* Return an offset into the constant string argument. */
- return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
- s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+ tem = fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
+ s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+ return fold_convert (type, tem);
}
if (p2[0] == '\0')
}
}
-/* Simplify a call to the strstr builtin.
+/* Simplify a call to the strchr builtin.
Return 0 if no simplification was possible, otherwise return the
simplified form of the call as a tree.
form of the builtin function call. */
static tree
-simplify_builtin_strchr (tree arglist)
+fold_builtin_strchr (tree arglist, tree type)
{
if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
{
char c;
const char *r;
+ tree tem;
if (target_char_cast (s2, &c))
return 0;
return build_int_cst (TREE_TYPE (s1), 0);
/* Return an offset into the constant string argument. */
- return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
- s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+ tem = fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
+ s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+ return fold_convert (type, tem);
}
-
- /* FIXME: Should use here strchrM optab so that ports can optimize
- this. */
return 0;
}
}
form of the builtin function call. */
static tree
-simplify_builtin_strrchr (tree arglist)
+fold_builtin_strrchr (tree arglist, tree type)
{
if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
{
char c;
const char *r;
+ tree tem;
if (target_char_cast (s2, &c))
return 0;
return build_int_cst (TREE_TYPE (s1), 0);
/* Return an offset into the constant string argument. */
- return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
- s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+ tem = fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
+ s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+ return fold_convert (type, tem);
}
if (! integer_zerop (s2))
form of the builtin function call. */
static tree
-simplify_builtin_strpbrk (tree arglist)
+fold_builtin_strpbrk (tree arglist, tree type)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
if (p1 != NULL)
{
const char *r = strpbrk (p1, p2);
+ tree tem;
if (r == NULL)
return build_int_cst (TREE_TYPE (s1), 0);
/* Return an offset into the constant string argument. */
- return fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
- s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+ tem = fold (build2 (PLUS_EXPR, TREE_TYPE (s1),
+ s1, build_int_cst (TREE_TYPE (s1), r - p1)));
+ return fold_convert (type, tem);
}
if (p2[0] == '\0')
form of the builtin function call. */
static tree
-simplify_builtin_strcat (tree arglist)
+fold_builtin_strcat (tree arglist)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
form of the builtin function call. */
static tree
-simplify_builtin_strncat (tree arglist)
+fold_builtin_strncat (tree arglist)
{
if (!validate_arglist (arglist,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
form of the builtin function call. */
static tree
-simplify_builtin_strspn (tree arglist)
+fold_builtin_strspn (tree arglist)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
form of the builtin function call. */
static tree
-simplify_builtin_strcspn (tree arglist)
+fold_builtin_strcspn (tree arglist)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
return build_function_call_expr (fn, arglist);
}
-static void
-simplify_builtin_va_start (tree arglist)
-{
- tree chain = TREE_CHAIN (arglist);
-
- if (TREE_CHAIN (chain))
- error ("too many arguments to function `va_start'");
-
- simplify_builtin_next_arg (chain);
-}
-
-static void
-simplify_builtin_next_arg (tree arglist)
+/* Fold the new_arg's arguments (ARGLIST). Returns true if there was an error
+ produced. False otherwise. This is done so that we don't output the error
+ or warning twice or three times. */
+bool
+fold_builtin_next_arg (tree arglist)
{
tree fntype = TREE_TYPE (current_function_decl);
if (TYPE_ARG_TYPES (fntype) == 0
|| (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
== void_type_node))
- error ("`va_start' used in function with fixed args");
- else if (arglist)
+ {
+ error ("%<va_start%> used in function with fixed args");
+ return true;
+ }
+ else if (!arglist)
+ {
+ /* Evidently an out of date version of <stdarg.h>; can't validate
+ va_start's second argument, but can still work as intended. */
+ warning (0, "%<__builtin_next_arg%> called without an argument");
+ return true;
+ }
+ /* We use __builtin_va_start (ap, 0, 0) or __builtin_next_arg (0, 0)
+ when we checked the arguments and if needed issued a warning. */
+ else if (!TREE_CHAIN (arglist)
+ || !integer_zerop (TREE_VALUE (arglist))
+ || !integer_zerop (TREE_VALUE (TREE_CHAIN (arglist)))
+ || TREE_CHAIN (TREE_CHAIN (arglist)))
{
tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl));
tree arg = TREE_VALUE (arglist);
+ if (TREE_CHAIN (arglist))
+ {
+ error ("%<va_start%> used with too many arguments");
+ return true;
+ }
+
/* Strip off all nops for the sake of the comparison. This
is not quite the same as STRIP_NOPS. It does more.
We must also strip off INDIRECT_EXPR for C++ reference
|| TREE_CODE (arg) == INDIRECT_REF)
arg = TREE_OPERAND (arg, 0);
if (arg != last_parm)
- warning ("second parameter of `va_start' not last named argument");
- TREE_VALUE (arglist) = arg;
+ {
+ /* FIXME: Sometimes with the tree optimizers we can get the
+ not the last argument even though the user used the last
+ argument. We just warn and set the arg to be the last
+ argument so that we will get wrong-code because of
+ it. */
+ warning (0, "second parameter of %<va_start%> not last named argument");
+ }
+ /* We want to verify the second parameter just once before the tree
+ optimizers are run and then avoid keeping it in the tree,
+ as otherwise we could warn even for correct code like:
+ void foo (int i, ...)
+ { va_list ap; i++; va_start (ap, i); va_end (ap); } */
+ TREE_VALUE (arglist) = integer_zero_node;
+ TREE_CHAIN (arglist) = build_tree_list (NULL, integer_zero_node);
}
- else
- /* Evidently an out of date version of <stdarg.h>; can't validate
- va_start's second argument, but can still work as intended. */
- warning ("`__builtin_next_arg' called without an argument");
+ return false;
}
the caller does not use the returned value of the function. */
static tree
-simplify_builtin_sprintf (tree arglist, int ignored)
+fold_builtin_sprintf (tree arglist, int ignored)
{
tree call, retval, dest, fmt;
const char *fmt_str = NULL;