#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
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 (void);
static rtx expand_builtin_va_start (tree);
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, tree, rtx, enum machine_mode, int);
-static rtx expand_builtin_memmove (tree, tree, rtx, enum machine_mode);
-static rtx expand_builtin_bcopy (tree, tree);
+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 builtin_strncpy_read_str (void *, HOST_WIDE_INT, enum machine_mode);
static rtx expand_builtin_strncpy (tree, rtx, enum machine_mode);
static rtx builtin_memset_read_str (void *, HOST_WIDE_INT, enum machine_mode);
static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, enum machine_mode);
-static rtx expand_builtin_memset (tree, rtx, enum machine_mode);
+static rtx expand_builtin_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_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_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);
+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_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);
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.
runtime. */
if (offset < 0 || offset > max)
{
- warning ("offset outside bounds of constant string");
+ warning (0, "offset outside bounds of constant string");
return 0;
}
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. */
/* Argument 1 must be either zero or one. */
if (INTVAL (op1) != 0 && INTVAL (op1) != 1)
{
- warning ("invalid second argument to %<__builtin_prefetch%>;"
+ warning (0, "invalid second argument to %<__builtin_prefetch%>;"
" using zero");
op1 = const0_rtx;
}
/* Argument 2 must be 0, 1, 2, or 3. */
if (INTVAL (op2) < 0 || INTVAL (op2) > 3)
{
- warning ("invalid third argument 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
case QUAL_UNION_TYPE: return union_type_class;
case ARRAY_TYPE: return (TYPE_STRING_FLAG (type)
? string_type_class : array_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 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 ();
}
{
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:
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;
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
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;
/* 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, word_mode, 0);
- if (GET_MODE (op1) != word_mode)
- op1 = convert_to_mode (word_mode, op1, 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, word_mode);
+ op0, mode, op1, mode2);
return target;
}
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);
/* 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)
{
static rtx
expand_builtin_memmove (tree arglist, tree type, rtx target,
- enum machine_mode mode)
+ enum machine_mode mode, tree orig_exp)
{
if (!validate_arglist (arglist,
POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
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,
if we failed the caller should emit a normal call. */
static rtx
-expand_builtin_bcopy (tree arglist, tree type)
+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, type, const0_rtx, VOIDmode);
+ return expand_builtin_memmove (newarglist, type, const0_rtx, VOIDmode, exp);
}
#ifndef HAVE_movstr
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);
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.
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;
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;
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. */
/* 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 ("%qT is promoted to %qT 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 %qT not %qT 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);
{
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 argument to %<__builtin_frame_address%>");
+ warning (0, "unsupported argument to %<__builtin_frame_address%>");
else
- warning ("unsupported argument to %<__builtin_return_address%>");
+ warning (0, "unsupported argument to %<__builtin_return_address%>");
return const0_rtx;
}
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 (!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 (!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
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 (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_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:
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:
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;
break;
case BUILT_IN_MEMMOVE:
- target = expand_builtin_memmove (arglist, TREE_TYPE (exp), 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, TREE_TYPE (exp));
+ 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;
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;
}
/* If we know this is a constant, emit the constant of one. */
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
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)
{
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))
+ if (flag_unsafe_math_optimizations)
{
- 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))
- {
- 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));
}
}
- /* 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)));
- /* 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))
- {
- 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);
+ 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);
+ }
}
+
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;
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);
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_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 (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_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. */
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')
{
/* 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");
+ 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)
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");
+ 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,