/* 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.
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_2 (tree, rtx, rtx);
static rtx expand_builtin_mathfn_3 (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_memset (tree, rtx, enum machine_mode);
static rtx expand_builtin_bzero (tree);
static rtx expand_builtin_strlen (tree, rtx, enum machine_mode);
-static rtx expand_builtin_strstr (tree, rtx, enum machine_mode);
-static rtx expand_builtin_strpbrk (tree, rtx, enum machine_mode);
-static rtx expand_builtin_strchr (tree, rtx, enum machine_mode);
-static rtx expand_builtin_strrchr (tree, rtx, enum machine_mode);
+static rtx expand_builtin_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_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_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);
+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 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);
-static tree fold_builtin_strstr (tree);
-static tree fold_builtin_strrchr (tree);
+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);
{
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)));
address located within it (depending on FNDECL_CODE). */
static rtx
-expand_builtin_return_addr (enum built_in_function fndecl_code, int count,
- rtx tem)
+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. */
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_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;
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;
+
+ 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. */
+
+ 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, word_mode, 0);
+ if (GET_MODE (op1) != word_mode)
+ op1 = convert_to_mode (word_mode, op1, 0);
+
+ target = emit_library_call_value (powi_optab->handlers[(int) mode].libfunc,
+ target, LCT_CONST_MAKE_BLOCK, mode, 2,
+ op0, mode, op1, word_mode);
+
+ 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. */
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))
{
- tree result = fold_builtin_strstr (arglist);
+ tree result = fold_builtin_strstr (arglist, type);
if (result)
return expand_expr (result, target, mode, EXPAND_NORMAL);
}
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))
{
- tree result = fold_builtin_strchr (arglist);
+ tree result = fold_builtin_strchr (arglist, type);
if (result)
return expand_expr (result, target, mode, EXPAND_NORMAL);
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))
{
- tree result = fold_builtin_strrchr (arglist);
+ tree result = fold_builtin_strrchr (arglist, type);
if (result)
return expand_expr (result, target, mode, EXPAND_NORMAL);
}
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))
{
- tree result = fold_builtin_strpbrk (arglist);
+ tree result = fold_builtin_strpbrk (arglist, type);
if (result)
return expand_expr (result, target, mode, EXPAND_NORMAL);
}
static rtx
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))
unsigned int dest_align
= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
rtx dest_mem, src_mem, dest_addr, len_rtx;
- tree result = fold_builtin_memcpy (exp);
+ tree result = fold_builtin_memcpy (fndecl, arglist);
if (result)
return expand_expr (result, target, mode, EXPAND_NORMAL);
static rtx
expand_builtin_strcpy (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, VOID_TYPE))
{
- tree result = fold_builtin_strcpy (exp, 0);
+ tree result = fold_builtin_strcpy (fndecl, arglist, 0);
if (result)
return expand_expr (result, target, mode, EXPAND_NORMAL);
static rtx
expand_builtin_strncpy (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))
{
tree slen = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)), 1);
tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
- tree result = fold_builtin_strncpy (exp, slen);
+ tree result = fold_builtin_strncpy (fndecl, arglist, slen);
if (result)
return expand_expr (result, target, mode, EXPAND_NORMAL);
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,
error ("too few arguments to function %<va_start%>");
return const0_rtx;
}
- if (TREE_CHAIN (chain))
- error ("too many arguments to function %<va_start%>");
if (fold_builtin_next_arg (chain))
- {
- return const0_rtx;
- }
+ 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);
{
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)
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
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 (! 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 (! 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)
{
- 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 = int_mode_for_mode (fmode);
+ if (imode == BLKmode)
+ return 0;
+ temp = gen_lowpart (imode, temp);
+ }
+ else
+ {
+ 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 (bitpos < 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 (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 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
the values. */
case BUILT_IN_CABS:
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;
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:
case BUILT_IN_NEXT_ARG:
if (fold_builtin_next_arg (arglist))
return const0_rtx;
- return expand_builtin_next_arg (arglist);
+ return expand_builtin_next_arg ();
case BUILT_IN_CLASSIFY_TYPE:
return expand_builtin_classify_type (arglist);
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;
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
simplification can be made. */
static tree
-fold_builtin_lround (tree exp)
+fold_builtin_lround (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;
}
}
- 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;
}
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));
}
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;
}
/* 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;
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 targetm.fold_builtin (exp, ignore);
+ 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);
return fold_builtin_fputs (arglist, ignore, true, NULL_TREE);
case BUILT_IN_STRSTR:
- return fold_builtin_strstr (arglist);
+ return fold_builtin_strstr (arglist, type);
case BUILT_IN_STRCAT:
return fold_builtin_strcat (arglist);
case BUILT_IN_STRCHR:
case BUILT_IN_INDEX:
- return fold_builtin_strchr (arglist);
+ return fold_builtin_strchr (arglist, type);
case BUILT_IN_STRRCHR:
case BUILT_IN_RINDEX:
- return fold_builtin_strrchr (arglist);
+ return fold_builtin_strrchr (arglist, type);
case BUILT_IN_STRCPY:
- return fold_builtin_strcpy (exp, NULL_TREE);
+ return fold_builtin_strcpy (fndecl, arglist, NULL_TREE);
case BUILT_IN_STRNCPY:
- return fold_builtin_strncpy (exp, NULL_TREE);
+ return fold_builtin_strncpy (fndecl, arglist, NULL_TREE);
case BUILT_IN_STRCMP:
return fold_builtin_strcmp (arglist);
return fold_builtin_strncmp (arglist);
case BUILT_IN_STRPBRK:
- return fold_builtin_strpbrk (arglist);
+ return fold_builtin_strpbrk (arglist, type);
case BUILT_IN_BCMP:
case BUILT_IN_MEMCMP:
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_LROUND:
case BUILT_IN_LROUNDF:
case BUILT_IN_LLROUND:
case BUILT_IN_LLROUNDF:
case BUILT_IN_LLROUNDL:
- return fold_builtin_lround (exp);
+ return fold_builtin_lround (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 (arglist, type, /*endp=*/1);
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);
+ return fold_builtin_unordered_cmp (fndecl, arglist, UNORDERED_EXPR,
+ NOP_EXPR);
/* 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 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;
}
form of the builtin function call. */
static tree
-fold_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')
form of the builtin function call. */
static tree
-fold_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);
}
return 0;
}
form of the builtin function call. */
static tree
-fold_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
-fold_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')
return build_function_call_expr (fn, arglist);
}
-/* Fold the new_arg's agruments (ARGLIST). Returns true if there was an error
+/* 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
error ("%<va_start%> used in function with fixed args");
return true;
}
- else if (arglist)
+ 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 ("%<__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
arg = TREE_OPERAND (arg, 0);
if (arg != last_parm)
{
- /* FIXME: Sometimes with the tree optimizaters 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. */
- arg = last_parm;
+ /* 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 ("second parameter of %<va_start%> not last named argument");
}
- TREE_VALUE (arglist) = arg;
- }
- 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 true;
+ /* 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);
}
return false;
}