static rtx c_readstr (const char *, enum machine_mode);
static int target_char_cast (tree, char *);
static rtx get_memory_rtx (tree);
+static tree build_string_literal (int, const char *);
static int apply_args_size (void);
static int apply_result_size (void);
#if defined (HAVE_untyped_call) || defined (HAVE_untyped_return)
static rtx expand_builtin_alloca (tree, rtx);
static rtx expand_builtin_unop (enum machine_mode, tree, rtx, rtx, optab);
static rtx expand_builtin_frame_address (tree, tree);
-static rtx expand_builtin_fputs (tree, int, int);
+static rtx expand_builtin_fputs (tree, rtx, bool);
+static rtx expand_builtin_printf (tree, rtx, enum machine_mode, bool);
+static rtx expand_builtin_fprintf (tree, rtx, enum machine_mode, bool);
static rtx expand_builtin_sprintf (tree, rtx, enum machine_mode);
static tree stabilize_va_list (tree, int);
static rtx expand_builtin_expect (tree, rtx);
static tree fold_builtin_inf (tree, int);
static tree fold_builtin_nan (tree, tree, int);
static int validate_arglist (tree, ...);
+static bool integer_valued_real_p (tree);
static tree fold_trunc_transparent_mathfn (tree);
static bool readonly_data_expr (tree);
static rtx expand_builtin_fabs (tree, rtx, rtx);
static rtx expand_builtin_cabs (tree, rtx);
static void init_builtin_dconsts (void);
static tree fold_builtin_cabs (tree, tree, tree);
+static tree fold_builtin_trunc (tree);
+static tree fold_builtin_floor (tree);
+static tree fold_builtin_ceil (tree);
+static tree fold_builtin_bitop (tree);
/* Initialize mathematical constants for constant folding builtins.
These constants need to be given to at least 160 bits precision. */
expand_builtin_setjmp_setup (buf_addr, next_lab);
- /* Set TARGET to zero and branch to the continue label. */
+ /* Set TARGET to zero and branch to the continue label. Use emit_jump to
+ ensure that pending stack adjustments are flushed. */
emit_move_insn (target, const0_rtx);
- emit_jump_insn (gen_jump (cont_lab));
- emit_barrier ();
+ emit_jump (cont_lab);
+
emit_label (next_lab);
expand_builtin_setjmp_receiver (next_lab);
if ((! (*insn_data[(int) CODE_FOR_prefetch].operand[0].predicate)
(op0,
insn_data[(int) CODE_FOR_prefetch].operand[0].mode))
- || (GET_MODE(op0) != Pmode))
+ || (GET_MODE (op0) != Pmode))
{
#ifdef POINTERS_EXTEND_UNSIGNED
- if (GET_MODE(op0) != Pmode)
+ if (GET_MODE (op0) != Pmode)
op0 = convert_memory_address (Pmode, op0);
#endif
op0 = force_reg (Pmode, op0);
int regno, size, align, nelts;
enum machine_mode mode;
rtx reg, mem;
- rtx *savevec = (rtx *) alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
+ rtx *savevec = alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
size = nelts = 0;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
/* We have taken care of the easy cases during constant folding. This
case is not obvious, so emit (constant_p_rtx (ARGLIST)) and let CSE
- get a chance to see if it can deduce whether ARGLIST is constant. */
+ get a chance to see if it can deduce whether ARGLIST is constant.
+ If CSE isn't going to run, of course, don't bother waiting. */
+
+ if (cse_not_expected)
+ return const0_rtx;
current_function_calls_constant_p = 1;
return tmp;
}
-/* Return mathematic function equivalent to FN but operating directly on TYPE,
- if available. */
+/* This helper macro, meant to be used in mathfn_built_in below,
+ determines which among a set of three builtin math functions is
+ appropriate for a given type mode. The `F' and `L' cases are
+ automatically generated from the `double' case. */
+#define CASE_MATHFN(BUILT_IN_MATHFN) \
+ case BUILT_IN_MATHFN: case BUILT_IN_MATHFN##F: case BUILT_IN_MATHFN##L: \
+ fcode = BUILT_IN_MATHFN; fcodef = BUILT_IN_MATHFN##F ; \
+ fcodel = BUILT_IN_MATHFN##L ; break;
+
+/* Return mathematic function equivalent to FN but operating directly
+ on TYPE, if available. If we can't do the conversion, return zero. */
tree
mathfn_built_in (tree type, enum built_in_function fn)
{
- enum built_in_function fcode = NOT_BUILT_IN;
- if (TYPE_MODE (type) == TYPE_MODE (double_type_node))
- switch (fn)
- {
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
- fcode = BUILT_IN_SQRT;
- break;
- case BUILT_IN_SIN:
- case BUILT_IN_SINF:
- case BUILT_IN_SINL:
- fcode = BUILT_IN_SIN;
- break;
- case BUILT_IN_COS:
- case BUILT_IN_COSF:
- case BUILT_IN_COSL:
- fcode = BUILT_IN_COS;
- break;
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
- fcode = BUILT_IN_EXP;
- break;
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
- fcode = BUILT_IN_LOG;
- break;
- case BUILT_IN_TAN:
- case BUILT_IN_TANF:
- case BUILT_IN_TANL:
- fcode = BUILT_IN_TAN;
- break;
- case BUILT_IN_ATAN:
- case BUILT_IN_ATANF:
- case BUILT_IN_ATANL:
- fcode = BUILT_IN_ATAN;
- break;
- case BUILT_IN_FLOOR:
- case BUILT_IN_FLOORF:
- case BUILT_IN_FLOORL:
- fcode = BUILT_IN_FLOOR;
- break;
- case BUILT_IN_CEIL:
- case BUILT_IN_CEILF:
- case BUILT_IN_CEILL:
- fcode = BUILT_IN_CEIL;
- break;
- case BUILT_IN_TRUNC:
- case BUILT_IN_TRUNCF:
- case BUILT_IN_TRUNCL:
- fcode = BUILT_IN_TRUNC;
- break;
- case BUILT_IN_ROUND:
- case BUILT_IN_ROUNDF:
- case BUILT_IN_ROUNDL:
- fcode = BUILT_IN_ROUND;
- break;
- case BUILT_IN_NEARBYINT:
- case BUILT_IN_NEARBYINTF:
- case BUILT_IN_NEARBYINTL:
- fcode = BUILT_IN_NEARBYINT;
- break;
- default:
- abort ();
- }
- else if (TYPE_MODE (type) == TYPE_MODE (float_type_node))
- switch (fn)
- {
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
- fcode = BUILT_IN_SQRTF;
- break;
- case BUILT_IN_SIN:
- case BUILT_IN_SINF:
- case BUILT_IN_SINL:
- fcode = BUILT_IN_SINF;
- break;
- case BUILT_IN_COS:
- case BUILT_IN_COSF:
- case BUILT_IN_COSL:
- fcode = BUILT_IN_COSF;
- break;
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
- fcode = BUILT_IN_EXPF;
- break;
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
- fcode = BUILT_IN_LOGF;
- break;
- case BUILT_IN_TAN:
- case BUILT_IN_TANF:
- case BUILT_IN_TANL:
- fcode = BUILT_IN_TANF;
- break;
- case BUILT_IN_ATAN:
- case BUILT_IN_ATANF:
- case BUILT_IN_ATANL:
- fcode = BUILT_IN_ATANF;
- break;
- case BUILT_IN_FLOOR:
- case BUILT_IN_FLOORF:
- case BUILT_IN_FLOORL:
- fcode = BUILT_IN_FLOORF;
- break;
- case BUILT_IN_CEIL:
- case BUILT_IN_CEILF:
- case BUILT_IN_CEILL:
- fcode = BUILT_IN_CEILF;
- break;
- case BUILT_IN_TRUNC:
- case BUILT_IN_TRUNCF:
- case BUILT_IN_TRUNCL:
- fcode = BUILT_IN_TRUNCF;
- break;
- case BUILT_IN_ROUND:
- case BUILT_IN_ROUNDF:
- case BUILT_IN_ROUNDL:
- fcode = BUILT_IN_ROUNDF;
- break;
- case BUILT_IN_NEARBYINT:
- case BUILT_IN_NEARBYINTF:
- case BUILT_IN_NEARBYINTL:
- fcode = BUILT_IN_NEARBYINTF;
- break;
- default:
- abort ();
- }
- else if (TYPE_MODE (type) == TYPE_MODE (long_double_type_node))
- switch (fn)
- {
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
- fcode = BUILT_IN_SQRTL;
- break;
- case BUILT_IN_SIN:
- case BUILT_IN_SINF:
- case BUILT_IN_SINL:
- fcode = BUILT_IN_SINL;
- break;
- case BUILT_IN_COS:
- case BUILT_IN_COSF:
- case BUILT_IN_COSL:
- fcode = BUILT_IN_COSL;
- break;
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
- fcode = BUILT_IN_EXPL;
- break;
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
- fcode = BUILT_IN_LOGL;
- break;
- case BUILT_IN_TAN:
- case BUILT_IN_TANF:
- case BUILT_IN_TANL:
- fcode = BUILT_IN_TANL;
- break;
- case BUILT_IN_ATAN:
- case BUILT_IN_ATANF:
- case BUILT_IN_ATANL:
- fcode = BUILT_IN_ATANL;
- break;
- case BUILT_IN_FLOOR:
- case BUILT_IN_FLOORF:
- case BUILT_IN_FLOORL:
- fcode = BUILT_IN_FLOORL;
- break;
- case BUILT_IN_CEIL:
- case BUILT_IN_CEILF:
- case BUILT_IN_CEILL:
- fcode = BUILT_IN_CEILL;
- break;
- case BUILT_IN_TRUNC:
- case BUILT_IN_TRUNCF:
- case BUILT_IN_TRUNCL:
- fcode = BUILT_IN_TRUNCL;
- break;
- case BUILT_IN_ROUND:
- case BUILT_IN_ROUNDF:
- case BUILT_IN_ROUNDL:
- fcode = BUILT_IN_ROUNDL;
- break;
- case BUILT_IN_NEARBYINT:
- case BUILT_IN_NEARBYINTF:
- case BUILT_IN_NEARBYINTL:
- fcode = BUILT_IN_NEARBYINTL;
- break;
+ const enum machine_mode type_mode = TYPE_MODE (type);
+ enum built_in_function fcode, fcodef, fcodel;
+
+ switch (fn)
+ {
+ CASE_MATHFN (BUILT_IN_ACOS)
+ CASE_MATHFN (BUILT_IN_ACOSH)
+ CASE_MATHFN (BUILT_IN_ASIN)
+ CASE_MATHFN (BUILT_IN_ASINH)
+ CASE_MATHFN (BUILT_IN_ATAN)
+ CASE_MATHFN (BUILT_IN_ATAN2)
+ CASE_MATHFN (BUILT_IN_ATANH)
+ CASE_MATHFN (BUILT_IN_CBRT)
+ CASE_MATHFN (BUILT_IN_CEIL)
+ CASE_MATHFN (BUILT_IN_COPYSIGN)
+ CASE_MATHFN (BUILT_IN_COS)
+ CASE_MATHFN (BUILT_IN_COSH)
+ CASE_MATHFN (BUILT_IN_DREM)
+ CASE_MATHFN (BUILT_IN_ERF)
+ CASE_MATHFN (BUILT_IN_ERFC)
+ CASE_MATHFN (BUILT_IN_EXP)
+ CASE_MATHFN (BUILT_IN_EXP10)
+ CASE_MATHFN (BUILT_IN_EXP2)
+ CASE_MATHFN (BUILT_IN_EXPM1)
+ CASE_MATHFN (BUILT_IN_FABS)
+ CASE_MATHFN (BUILT_IN_FDIM)
+ CASE_MATHFN (BUILT_IN_FLOOR)
+ CASE_MATHFN (BUILT_IN_FMA)
+ CASE_MATHFN (BUILT_IN_FMAX)
+ CASE_MATHFN (BUILT_IN_FMIN)
+ CASE_MATHFN (BUILT_IN_FMOD)
+ CASE_MATHFN (BUILT_IN_FREXP)
+ CASE_MATHFN (BUILT_IN_GAMMA)
+ CASE_MATHFN (BUILT_IN_HUGE_VAL)
+ CASE_MATHFN (BUILT_IN_HYPOT)
+ CASE_MATHFN (BUILT_IN_ILOGB)
+ CASE_MATHFN (BUILT_IN_INF)
+ CASE_MATHFN (BUILT_IN_J0)
+ CASE_MATHFN (BUILT_IN_J1)
+ CASE_MATHFN (BUILT_IN_JN)
+ CASE_MATHFN (BUILT_IN_LDEXP)
+ CASE_MATHFN (BUILT_IN_LGAMMA)
+ CASE_MATHFN (BUILT_IN_LLRINT)
+ CASE_MATHFN (BUILT_IN_LLROUND)
+ CASE_MATHFN (BUILT_IN_LOG)
+ CASE_MATHFN (BUILT_IN_LOG10)
+ CASE_MATHFN (BUILT_IN_LOG1P)
+ CASE_MATHFN (BUILT_IN_LOG2)
+ CASE_MATHFN (BUILT_IN_LOGB)
+ CASE_MATHFN (BUILT_IN_LRINT)
+ CASE_MATHFN (BUILT_IN_LROUND)
+ CASE_MATHFN (BUILT_IN_MODF)
+ CASE_MATHFN (BUILT_IN_NAN)
+ CASE_MATHFN (BUILT_IN_NANS)
+ CASE_MATHFN (BUILT_IN_NEARBYINT)
+ CASE_MATHFN (BUILT_IN_NEXTAFTER)
+ CASE_MATHFN (BUILT_IN_NEXTTOWARD)
+ CASE_MATHFN (BUILT_IN_POW)
+ CASE_MATHFN (BUILT_IN_POW10)
+ CASE_MATHFN (BUILT_IN_REMAINDER)
+ CASE_MATHFN (BUILT_IN_REMQUO)
+ CASE_MATHFN (BUILT_IN_RINT)
+ CASE_MATHFN (BUILT_IN_ROUND)
+ CASE_MATHFN (BUILT_IN_SCALB)
+ CASE_MATHFN (BUILT_IN_SCALBLN)
+ CASE_MATHFN (BUILT_IN_SCALBN)
+ CASE_MATHFN (BUILT_IN_SIGNIFICAND)
+ CASE_MATHFN (BUILT_IN_SIN)
+ CASE_MATHFN (BUILT_IN_SINCOS)
+ CASE_MATHFN (BUILT_IN_SINH)
+ CASE_MATHFN (BUILT_IN_SQRT)
+ CASE_MATHFN (BUILT_IN_TAN)
+ CASE_MATHFN (BUILT_IN_TANH)
+ CASE_MATHFN (BUILT_IN_TGAMMA)
+ CASE_MATHFN (BUILT_IN_TRUNC)
+ CASE_MATHFN (BUILT_IN_Y0)
+ CASE_MATHFN (BUILT_IN_Y1)
+ CASE_MATHFN (BUILT_IN_YN)
+
default:
- abort ();
+ return 0;
}
- return implicit_built_in_decls[fcode];
+
+ if (type_mode == TYPE_MODE (double_type_node))
+ return implicit_built_in_decls[fcode];
+ else if (type_mode == TYPE_MODE (float_type_node))
+ return implicit_built_in_decls[fcodef];
+ else if (type_mode == TYPE_MODE (long_double_type_node))
+ return implicit_built_in_decls[fcodel];
+ else
+ return 0;
}
/* If errno must be maintained, expand the RTL to check if the result,
expand_builtin_mathfn (tree exp, rtx target, rtx subtarget)
{
optab builtin_optab;
- rtx op0, insns;
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ rtx op0, insns, before_call;
+ tree fndecl = get_callee_fndecl (exp);
tree arglist = TREE_OPERAND (exp, 1);
enum machine_mode mode;
bool errno_set = false;
/* Make a suitable register to place result in. */
mode = TYPE_MODE (TREE_TYPE (exp));
- /* Before working hard, check whether the instruction is available. */
- if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
- return 0;
- target = gen_reg_rtx (mode);
-
if (! flag_errno_math || ! HONOR_NANS (mode))
errno_set = false;
- /* Wrap the computation of the argument in a SAVE_EXPR, as we may
- need to expand the argument again. This way, we will not perform
- side-effects more the once. */
- narg = save_expr (arg);
- if (narg != arg)
+ /* Before working hard, check whether the instruction is available. */
+ if (builtin_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
{
- arglist = build_tree_list (NULL_TREE, arg);
- exp = build_function_call_expr (fndecl, arglist);
- }
+ target = gen_reg_rtx (mode);
- op0 = expand_expr (arg, subtarget, VOIDmode, 0);
+ /* Wrap the computation of the argument in a SAVE_EXPR, as we may
+ need to expand the argument again. This way, we will not perform
+ side-effects more the once. */
+ narg = save_expr (arg);
+ if (narg != arg)
+ {
+ arglist = build_tree_list (NULL_TREE, arg);
+ exp = build_function_call_expr (fndecl, arglist);
+ }
- emit_queue ();
- start_sequence ();
+ op0 = expand_expr (arg, subtarget, VOIDmode, 0);
- /* Compute into TARGET.
- Set TARGET to wherever the result comes back. */
- target = expand_unop (mode, builtin_optab, op0, target, 0);
+ emit_queue ();
+ start_sequence ();
- /* If we were unable to expand via the builtin, stop the sequence
- (without outputting the insns) and call to the library function
- with the stabilized argument list. */
- if (target == 0)
- {
+ /* Compute into TARGET.
+ Set TARGET to wherever the result comes back. */
+ target = expand_unop (mode, builtin_optab, op0, target, 0);
+
+ if (target != 0)
+ {
+ if (errno_set)
+ expand_errno_check (exp, target);
+
+ /* Output the entire sequence. */
+ insns = get_insns ();
+ end_sequence ();
+ emit_insn (insns);
+ return target;
+ }
+
+ /* If we were unable to expand via the builtin, stop the sequence
+ (without outputting the insns) and call to the library function
+ with the stabilized argument list. */
end_sequence ();
- return expand_call (exp, target, target == const0_rtx);
}
- if (errno_set)
- expand_errno_check (exp, target);
+ before_call = get_last_insn ();
- /* Output the entire sequence. */
- insns = get_insns ();
- end_sequence ();
- emit_insn (insns);
+ target = expand_call (exp, target, target == const0_rtx);
+
+ /* If this is a sqrt operation and we don't care about errno, try to
+ attach a REG_EQUAL note with a SQRT rtx to the emitted libcall.
+ This allows the semantics of the libcall to be visible to the RTL
+ optimizers. */
+ if (builtin_optab == sqrt_optab && !errno_set)
+ {
+ /* Search backwards through the insns emitted by expand_call looking
+ for the instruction with the REG_RETVAL note. */
+ rtx last = get_last_insn ();
+ while (last != before_call)
+ {
+ if (find_reg_note (last, REG_RETVAL, NULL))
+ {
+ rtx note = find_reg_note (last, REG_EQUAL, NULL);
+ /* Check that the REQ_EQUAL note is an EXPR_LIST with
+ two elements, i.e. symbol_ref(sqrt) and the operand. */
+ if (note
+ && GET_CODE (note) == EXPR_LIST
+ && GET_CODE (XEXP (note, 0)) == EXPR_LIST
+ && XEXP (XEXP (note, 0), 1) != NULL_RTX
+ && XEXP (XEXP (XEXP (note, 0), 1), 1) == NULL_RTX)
+ {
+ rtx operand = XEXP (XEXP (XEXP (note, 0), 1), 0);
+ /* Check operand is a register with expected mode. */
+ if (operand
+ && GET_CODE (operand) == REG
+ && GET_MODE (operand) == mode)
+ {
+ /* Replace the REG_EQUAL note with a SQRT rtx. */
+ rtx equiv = gen_rtx_SQRT (mode, operand);
+ set_unique_reg_note (last, REG_EQUAL, equiv);
+ }
+ }
+ break;
+ }
+ last = PREV_INSN (last);
+ }
+ }
return target;
}
{
optab builtin_optab;
rtx op0, op1, insns;
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree fndecl = get_callee_fndecl (exp);
tree arglist = TREE_OPERAND (exp, 1);
tree arg0, arg1, temp, narg;
enum machine_mode mode;
val = (n < 0) ? -n : n;
- memset (cache, 0, sizeof(cache));
+ memset (cache, 0, sizeof (cache));
cache[1] = x;
result = expand_powi_1 (mode, (n < 0) ? -n : n, cache);
arg0 = TREE_VALUE (arglist);
arg1 = TREE_VALUE (TREE_CHAIN (arglist));
- if (flag_unsafe_math_optimizations
- && ! flag_errno_math
- && ! optimize_size
- && TREE_CODE (arg1) == REAL_CST
+ if (TREE_CODE (arg1) == REAL_CST
&& ! TREE_CONSTANT_OVERFLOW (arg1))
{
REAL_VALUE_TYPE cint;
c = TREE_REAL_CST (arg1);
n = real_to_integer (&c);
real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0);
- if (real_identical (&c, &cint)
- && powi_cost (n) <= POWI_MAX_MULTS)
+ if (real_identical (&c, &cint))
{
- enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
- rtx op = expand_expr (arg0, subtarget, VOIDmode, 0);
- op = force_reg (mode, op);
- return expand_powi (op, mode, n);
+ /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact.
+ Otherwise, check the number of multiplications required.
+ Note that pow never sets errno for an integer exponent. */
+ if ((n >= -1 && n <= 2)
+ || (flag_unsafe_math_optimizations
+ && ! optimize_size
+ && powi_cost (n) <= POWI_MAX_MULTS))
+ {
+ enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
+ rtx op = expand_expr (arg0, subtarget, VOIDmode, 0);
+ op = force_reg (mode, op);
+ return expand_powi (op, mode, n);
+ }
}
}
return expand_builtin_mathfn_2 (exp, target, NULL_RTX);
return expand_expr (result, target, mode, EXPAND_NORMAL);
}
-#ifdef HAVE_cmpstrsi
+#if defined HAVE_cmpmemsi || defined HAVE_cmpstrsi
{
rtx arg1_rtx, arg2_rtx, arg3_rtx;
rtx result;
= get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
int arg2_align
= get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
- enum machine_mode insn_mode
- = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
+ enum machine_mode insn_mode;
+
+#ifdef HAVE_cmpmemsi
+ if (HAVE_cmpmemsi)
+ insn_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode;
+ else
+#endif
+#ifdef HAVE_cmpstrsi
+ if (HAVE_cmpstrsi)
+ insn_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
+ else
+#endif
+ return 0;
/* If we don't have POINTER_TYPE, call the function. */
if (arg1_align == 0 || arg2_align == 0)
arg1_rtx = get_memory_rtx (arg1);
arg2_rtx = get_memory_rtx (arg2);
arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
- if (!HAVE_cmpstrsi)
- insn = NULL_RTX;
+#ifdef HAVE_cmpmemsi
+ if (HAVE_cmpmemsi)
+ insn = gen_cmpmemsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
+ GEN_INT (MIN (arg1_align, arg2_align)));
else
+#endif
+#ifdef HAVE_cmpstrsi
+ if (HAVE_cmpstrsi)
insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
GEN_INT (MIN (arg1_align, arg2_align)));
+ else
+#endif
+ abort ();
if (insn)
emit_insn (insn);
src = TREE_VALUE (TREE_CHAIN (arglist));
const char *p = c_getstr (src);
- /* If the string length is zero, return the dst parameter. */
- if (p && *p == '\0')
- return expand_expr (dst, target, mode, EXPAND_NORMAL);
+ if (p)
+ {
+ /* If the string length is zero, return the dst parameter. */
+ if (*p == '\0')
+ return expand_expr (dst, target, mode, EXPAND_NORMAL);
+ else if (!optimize_size)
+ {
+ /* Otherwise if !optimize_size, see if we can store by
+ pieces into (dst + strlen(dst)). */
+ tree newdst, arglist,
+ strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN];
+
+ /* This is the length argument. */
+ arglist = build_tree_list (NULL_TREE,
+ fold (size_binop (PLUS_EXPR,
+ c_strlen (src, 0),
+ ssize_int (1))));
+ /* Prepend src argument. */
+ arglist = tree_cons (NULL_TREE, src, arglist);
+
+ /* We're going to use dst more than once. */
+ dst = save_expr (dst);
+
+ /* Create strlen (dst). */
+ newdst =
+ fold (build_function_call_expr (strlen_fn,
+ build_tree_list (NULL_TREE,
+ dst)));
+ /* Create (dst + strlen (dst)). */
+ newdst = fold (build (PLUS_EXPR, TREE_TYPE (dst), dst, newdst));
+
+ /* Prepend the new dst argument. */
+ arglist = tree_cons (NULL_TREE, newdst, arglist);
+
+ /* We don't want to get turned into a memcpy if the
+ target is const0_rtx, i.e. when the return value
+ isn't used. That would produce pessimized code so
+ pass in a target of zero, it should never actually be
+ used. If this was successful return the original
+ dst, not the result of mempcpy. */
+ if (expand_builtin_mempcpy (arglist, /*target=*/0, mode, /*endp=*/0))
+ return expand_expr (dst, target, mode, EXPAND_NORMAL);
+ else
+ return 0;
+ }
+ }
return 0;
}
long, we attempt to transform this call into __builtin_fputc(). */
static rtx
-expand_builtin_fputs (tree arglist, int ignore, int unlocked)
+expand_builtin_fputs (tree arglist, rtx target, bool unlocked)
{
tree len, fn;
tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
/* If the return value is used, or the replacement _DECL isn't
initialized, don't do the transformation. */
- if (!ignore || !fn_fputc || !fn_fwrite)
+ if (target != const0_rtx || !fn_fputc || !fn_fwrite)
return 0;
/* Verify the arguments in the original call. */
}
return expand_expr (build_function_call_expr (fn, arglist),
- (ignore ? const0_rtx : NULL_RTX),
- VOIDmode, EXPAND_NORMAL);
+ const0_rtx, VOIDmode, EXPAND_NORMAL);
}
/* Expand a call to __builtin_expect. We return our argument and emit a
&& (integer_zerop (arg1) || integer_onep (arg1)))
{
int num_jumps = 0;
+ int save_pending_stack_adjust = pending_stack_adjust;
rtx insn;
/* If we fail to locate an appropriate conditional jump, we'll
/* If no jumps were modified, fail and do __builtin_expect the normal
way. */
if (num_jumps == 0)
- ret = NULL_RTX;
+ {
+ ret = NULL_RTX;
+ pending_stack_adjust = save_pending_stack_adjust;
+ }
}
return ret;
return expand_complex_abs (mode, op0, target, 0);
}
+/* Create a new constant string literal and return a char* pointer to it.
+ The STRING_CST value is the LEN characters at STR. */
+static tree
+build_string_literal (int len, const char *str)
+{
+ tree t, elem, index, type;
+
+ t = build_string (len, str);
+ elem = build_type_variant (char_type_node, 1, 0);
+ index = build_index_type (build_int_2 (len - 1, 0));
+ type = build_array_type (elem, index);
+ TREE_TYPE (t) = type;
+ TREE_CONSTANT (t) = 1;
+ TREE_READONLY (t) = 1;
+ TREE_STATIC (t) = 1;
+
+ type = build_pointer_type (type);
+ t = build1 (ADDR_EXPR, type, t);
+
+ type = build_pointer_type (elem);
+ t = build1 (NOP_EXPR, type, t);
+ return t;
+}
+
+/* Expand a call to printf or printf_unlocked with argument list ARGLIST.
+ Return 0 if a normal call should be emitted rather than transforming
+ the function inline. If convenient, the result should be placed in
+ TARGET with mode MODE. UNLOCKED indicates this is a printf_unlocked
+ call. */
+static rtx
+expand_builtin_printf (tree arglist, rtx target, enum machine_mode mode,
+ bool unlocked)
+{
+ tree fn_putchar = unlocked
+ ? implicit_built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_PUTCHAR];
+ tree fn_puts = unlocked ? implicit_built_in_decls[BUILT_IN_PUTS_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_PUTS];
+ const char *fmt_str;
+ tree fn, fmt, arg;
+
+ /* If the return value is used, don't do the transformation. */
+ if (target != const0_rtx)
+ return 0;
+
+ /* Verify the required arguments in the original call. */
+ if (! arglist)
+ return 0;
+ fmt = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return 0;
+
+ /* If the format specifier was "%s\n", call __builtin_puts(arg). */
+ if (strcmp (fmt_str, "%s\n") == 0)
+ {
+ if (! arglist
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist))
+ return 0;
+ fn = fn_puts;
+ }
+ /* If the format specifier was "%c", call __builtin_putchar(arg). */
+ else if (strcmp (fmt_str, "%c") == 0)
+ {
+ if (! arglist
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
+ || TREE_CHAIN (arglist))
+ return 0;
+ fn = fn_putchar;
+ }
+ else
+ {
+ /* We can't handle anything else with % args or %% ... yet. */
+ if (strchr (fmt_str, '%'))
+ return 0;
+
+ if (arglist)
+ return 0;
+
+ /* If the format specifier was "", printf does nothing. */
+ if (fmt_str[0] == '\0')
+ return const0_rtx;
+ /* If the format specifier has length of 1, call putchar. */
+ if (fmt_str[1] == '\0')
+ {
+ /* Given printf("c"), (where c is any one character,)
+ convert "c"[0] to an int and pass that to the replacement
+ function. */
+ arg = build_int_2 (fmt_str[0], 0);
+ arglist = build_tree_list (NULL_TREE, arg);
+ fn = fn_putchar;
+ }
+ else
+ {
+ /* If the format specifier was "string\n", call puts("string"). */
+ size_t len = strlen (fmt_str);
+ if (fmt_str[len - 1] == '\n')
+ {
+ /* Create a NUL-terminated string that's one char shorter
+ than the original, stripping off the trailing '\n'. */
+ char *newstr = (char *) alloca (len);
+ memcpy (newstr, fmt_str, len - 1);
+ newstr[len - 1] = 0;
+
+ arg = build_string_literal (len, newstr);
+ arglist = build_tree_list (NULL_TREE, arg);
+ fn = fn_puts;
+ }
+ else
+ /* We'd like to arrange to call fputs(string,stdout) here,
+ but we need stdout and don't have a way to get it yet. */
+ return 0;
+ }
+ }
+
+ if (!fn)
+ return 0;
+ return expand_expr (build_function_call_expr (fn, arglist),
+ target, mode, EXPAND_NORMAL);
+}
+
+/* Expand a call to fprintf or fprintf_unlocked with argument list ARGLIST.
+ Return 0 if a normal call should be emitted rather than transforming
+ the function inline. If convenient, the result should be placed in
+ TARGET with mode MODE. UNLOCKED indicates this is a fprintf_unlocked
+ call. */
+static rtx
+expand_builtin_fprintf (tree arglist, rtx target, enum machine_mode mode,
+ bool unlocked)
+{
+ tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_FPUTC];
+ tree fn_fputs = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTS_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_FPUTS];
+ const char *fmt_str;
+ tree fn, fmt, fp, arg;
+
+ /* If the return value is used, don't do the transformation. */
+ if (target != const0_rtx)
+ return 0;
+
+ /* Verify the required arguments in the original call. */
+ if (! arglist)
+ return 0;
+ fp = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (fp)) != POINTER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return 0;
+ fmt = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return 0;
+
+ /* If the format specifier was "%s", call __builtin_fputs(arg,fp). */
+ if (strcmp (fmt_str, "%s") == 0)
+ {
+ if (! arglist
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist))
+ return 0;
+ arg = TREE_VALUE (arglist);
+ arglist = build_tree_list (NULL_TREE, fp);
+ arglist = tree_cons (NULL_TREE, arg, arglist);
+ fn = fn_fputs;
+ }
+ /* If the format specifier was "%c", call __builtin_fputc(arg,fp). */
+ else if (strcmp (fmt_str, "%c") == 0)
+ {
+ if (! arglist
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
+ || TREE_CHAIN (arglist))
+ return 0;
+ arg = TREE_VALUE (arglist);
+ arglist = build_tree_list (NULL_TREE, fp);
+ arglist = tree_cons (NULL_TREE, arg, arglist);
+ fn = fn_fputc;
+ }
+ else
+ {
+ /* We can't handle anything else with % args or %% ... yet. */
+ if (strchr (fmt_str, '%'))
+ return 0;
+
+ if (arglist)
+ return 0;
+
+ /* If the format specifier was "", fprintf does nothing. */
+ if (fmt_str[0] == '\0')
+ {
+ /* Evaluate and ignore FILE* argument for side-effects. */
+ expand_expr (fp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ return const0_rtx;
+ }
+
+ /* When "string" doesn't contain %, replace all cases of
+ fprintf(stream,string) with fputs(string,stream). The fputs
+ builtin will take care of special cases like length == 1. */
+ arglist = build_tree_list (NULL_TREE, fp);
+ arglist = tree_cons (NULL_TREE, fmt, arglist);
+ fn = fn_fputs;
+ }
+
+ if (!fn)
+ return 0;
+ return expand_expr (build_function_call_expr (fn, arglist),
+ target, mode, EXPAND_NORMAL);
+}
+
/* Expand a call to sprintf with argument list ARGLIST. Return 0 if
a normal call should be emitted rather than expanding the function
inline. If convenient, the result should be placed in TARGET with
if (! arglist)
return 0;
fmt = TREE_VALUE (arglist);
- if (TREE_CODE (TREE_TYPE (dest)) != POINTER_TYPE)
+ if (TREE_CODE (TREE_TYPE (fmt)) != POINTER_TYPE)
return 0;
arglist = TREE_CHAIN (arglist);
expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
int ignore)
{
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree fndecl = get_callee_fndecl (exp);
tree arglist = TREE_OPERAND (exp, 1);
enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
enum machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp));
- /* Perform postincrements before expanding builtin functions. */
+ /* Perform postincrements before expanding builtin functions. */
emit_queue ();
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
/* When not optimizing, generate calls to library functions for a certain
set of builtins. */
- if (!optimize && !CALLED_AS_BUILT_IN (fndecl))
- switch (fcode)
- {
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
- case BUILT_IN_SIN:
- case BUILT_IN_SINF:
- case BUILT_IN_SINL:
- case BUILT_IN_COS:
- case BUILT_IN_COSF:
- case BUILT_IN_COSL:
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
- case BUILT_IN_TAN:
- case BUILT_IN_TANF:
- case BUILT_IN_TANL:
- case BUILT_IN_ATAN:
- case BUILT_IN_ATANF:
- case BUILT_IN_ATANL:
- case BUILT_IN_POW:
- case BUILT_IN_POWF:
- case BUILT_IN_POWL:
- case BUILT_IN_ATAN2:
- case BUILT_IN_ATAN2F:
- case BUILT_IN_ATAN2L:
- case BUILT_IN_MEMSET:
- case BUILT_IN_MEMCPY:
- case BUILT_IN_MEMCMP:
- case BUILT_IN_MEMPCPY:
- case BUILT_IN_MEMMOVE:
- case BUILT_IN_BCMP:
- case BUILT_IN_BZERO:
- case BUILT_IN_BCOPY:
- case BUILT_IN_INDEX:
- case BUILT_IN_RINDEX:
- case BUILT_IN_SPRINTF:
- case BUILT_IN_STPCPY:
- case BUILT_IN_STRCHR:
- case BUILT_IN_STRRCHR:
- case BUILT_IN_STRLEN:
- case BUILT_IN_STRCPY:
- case BUILT_IN_STRNCPY:
- case BUILT_IN_STRNCMP:
- case BUILT_IN_STRSTR:
- case BUILT_IN_STRPBRK:
- case BUILT_IN_STRCAT:
- case BUILT_IN_STRNCAT:
- case BUILT_IN_STRSPN:
- case BUILT_IN_STRCSPN:
- case BUILT_IN_STRCMP:
- case BUILT_IN_FFS:
- case BUILT_IN_PUTCHAR:
- case BUILT_IN_PUTS:
- case BUILT_IN_PRINTF:
- case BUILT_IN_FPUTC:
- case BUILT_IN_FPUTS:
- case BUILT_IN_FWRITE:
- case BUILT_IN_PUTCHAR_UNLOCKED:
- case BUILT_IN_PUTS_UNLOCKED:
- case BUILT_IN_PRINTF_UNLOCKED:
- case BUILT_IN_FPUTC_UNLOCKED:
- case BUILT_IN_FPUTS_UNLOCKED:
- case BUILT_IN_FWRITE_UNLOCKED:
- case BUILT_IN_FLOOR:
- case BUILT_IN_FLOORF:
- case BUILT_IN_FLOORL:
- case BUILT_IN_CEIL:
- case BUILT_IN_CEILF:
- case BUILT_IN_CEILL:
- case BUILT_IN_TRUNC:
- case BUILT_IN_TRUNCF:
- case BUILT_IN_TRUNCL:
- case BUILT_IN_ROUND:
- case BUILT_IN_ROUNDF:
- case BUILT_IN_ROUNDL:
- case BUILT_IN_NEARBYINT:
- case BUILT_IN_NEARBYINTF:
- case BUILT_IN_NEARBYINTL:
- return expand_call (exp, target, ignore);
-
- default:
- break;
- }
+ if (!optimize
+ && !CALLED_AS_BUILT_IN (fndecl)
+ && DECL_ASSEMBLER_NAME_SET_P (fndecl)
+ && fcode != BUILT_IN_ALLOCA)
+ return expand_call (exp, target, ignore);
/* The built-in function expanders test for target == const0_rtx
to determine whether the function's result will be ignored. */
expand_builtin_trap ();
return const0_rtx;
+ case BUILT_IN_PRINTF:
+ target = expand_builtin_printf (arglist, target, mode, false);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_PRINTF_UNLOCKED:
+ target = expand_builtin_printf (arglist, target, mode, true);
+ if (target)
+ return target;
+ break;
+
case BUILT_IN_FPUTS:
- target = expand_builtin_fputs (arglist, ignore,/*unlocked=*/ 0);
+ target = expand_builtin_fputs (arglist, target, false);
if (target)
return target;
break;
+
case BUILT_IN_FPUTS_UNLOCKED:
- target = expand_builtin_fputs (arglist, ignore,/*unlocked=*/ 1);
+ target = expand_builtin_fputs (arglist, target, true);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FPRINTF:
+ target = expand_builtin_fprintf (arglist, target, mode, false);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_FPRINTF_UNLOCKED:
+ target = expand_builtin_fprintf (arglist, target, mode, true);
if (target)
return target;
break;
|| TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR)
return END_BUILTINS;
- fndecl = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
- if (TREE_CODE (fndecl) != FUNCTION_DECL
+ fndecl = get_callee_fndecl (t);
+ if (fndecl == NULL_TREE
|| ! DECL_BUILT_IN (fndecl)
|| DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
return END_BUILTINS;
&& TREE_CODE (TREE_OPERAND (arglist, 0)) == STRING_CST))
return integer_one_node;
- /* If we aren't going to be running CSE or this expression
- has side effects, show we don't know it to be a constant.
- Likewise if it's a pointer or aggregate type since in those
- case we only want literals, since those are only optimized
+ /* If this expression has side effects, show we don't know it to be a
+ constant. Likewise if it's a pointer or aggregate type since in
+ those case we only want literals, since those are only optimized
when generating RTL, not later.
And finally, if we are compiling an initializer, not code, we
need to return a definite result now; there's not going to be any
more optimization done. */
- if (TREE_SIDE_EFFECTS (arglist) || cse_not_expected
+ if (TREE_SIDE_EFFECTS (arglist)
|| AGGREGATE_TYPE_P (TREE_TYPE (arglist))
|| POINTER_TYPE_P (TREE_TYPE (arglist))
|| cfun == 0)
return build_real (type, real);
}
-/* EXP is assumed to me builtin call where truncation can be propagated
+/* Return true if the floating point expression T has an integer value.
+ We also allow +Inf, -Inf and NaN to be considered integer values. */
+
+static bool
+integer_valued_real_p (tree t)
+{
+ switch (TREE_CODE (t))
+ {
+ case FLOAT_EXPR:
+ return true;
+
+ case ABS_EXPR:
+ case SAVE_EXPR:
+ case NON_LVALUE_EXPR:
+ return integer_valued_real_p (TREE_OPERAND (t, 0));
+
+ case COMPOUND_EXPR:
+ case MODIFY_EXPR:
+ case BIND_EXPR:
+ return integer_valued_real_p (TREE_OPERAND (t, 1));
+
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ case MULT_EXPR:
+ case MIN_EXPR:
+ case MAX_EXPR:
+ return integer_valued_real_p (TREE_OPERAND (t, 0))
+ && integer_valued_real_p (TREE_OPERAND (t, 1));
+
+ case COND_EXPR:
+ return integer_valued_real_p (TREE_OPERAND (t, 1))
+ && integer_valued_real_p (TREE_OPERAND (t, 2));
+
+ case REAL_CST:
+ if (! TREE_CONSTANT_OVERFLOW (t))
+ {
+ REAL_VALUE_TYPE c, cint;
+
+ c = TREE_REAL_CST (t);
+ real_trunc (&cint, TYPE_MODE (TREE_TYPE (t)), &c);
+ return real_identical (&c, &cint);
+ }
+
+ case NOP_EXPR:
+ {
+ tree type = TREE_TYPE (TREE_OPERAND (t, 0));
+ if (TREE_CODE (type) == INTEGER_TYPE)
+ return true;
+ if (TREE_CODE (type) == REAL_TYPE)
+ return integer_valued_real_p (TREE_OPERAND (t, 0));
+ break;
+ }
+
+ case CALL_EXPR:
+ switch (builtin_mathfn_code (t))
+ {
+ case BUILT_IN_CEIL:
+ case BUILT_IN_CEILF:
+ case BUILT_IN_CEILL:
+ case BUILT_IN_FLOOR:
+ case BUILT_IN_FLOORF:
+ case BUILT_IN_FLOORL:
+ case BUILT_IN_NEARBYINT:
+ case BUILT_IN_NEARBYINTF:
+ case BUILT_IN_NEARBYINTL:
+ case BUILT_IN_ROUND:
+ case BUILT_IN_ROUNDF:
+ case BUILT_IN_ROUNDL:
+ case BUILT_IN_TRUNC:
+ case BUILT_IN_TRUNCF:
+ case BUILT_IN_TRUNCL:
+ return true;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+/* EXP is assumed to be builtin call where truncation can be propagated
across (for instance floor((double)f) == (double)floorf (f).
Do the transformation. */
+
static tree
fold_trunc_transparent_mathfn (tree exp)
{
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ 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 && validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ arg = TREE_VALUE (arglist);
+ /* Integer rounding functions are idempotent. */
+ if (fcode == builtin_mathfn_code (arg))
+ return arg;
+
+ /* If argument is already integer valued, and we don't need to worry
+ about setting errno, there's no need to perform rounding. */
+ if (! flag_errno_math && integer_valued_real_p (arg))
+ return arg;
+
+ if (optimize)
{
- tree arg0 = strip_float_extensions (TREE_VALUE (arglist));
+ tree arg0 = strip_float_extensions (arg);
tree ftype = TREE_TYPE (exp);
tree newtype = TREE_TYPE (arg0);
tree decl;
return NULL_TREE;
}
+/* Fold function call to builtin trunc, truncf or truncl. Return
+ NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_trunc (tree exp)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg;
+
+ if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ /* Optimize trunc of constant value. */
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ REAL_VALUE_TYPE r, x;
+ tree type = TREE_TYPE (exp);
+
+ x = TREE_REAL_CST (arg);
+ real_trunc (&r, TYPE_MODE (type), &x);
+ return build_real (type, r);
+ }
+
+ return fold_trunc_transparent_mathfn (exp);
+}
+
+/* Fold function call to builtin floor, floorf or floorl. Return
+ NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_floor (tree exp)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg;
+
+ if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ /* Optimize floor of constant value. */
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ REAL_VALUE_TYPE x;
+
+ x = TREE_REAL_CST (arg);
+ if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
+ {
+ tree type = TREE_TYPE (exp);
+ REAL_VALUE_TYPE r;
+
+ real_floor (&r, TYPE_MODE (type), &x);
+ return build_real (type, r);
+ }
+ }
+
+ return fold_trunc_transparent_mathfn (exp);
+}
+
+/* Fold function call to builtin ceil, ceilf or ceill. Return
+ NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_ceil (tree exp)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg;
+
+ if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ /* Optimize ceil of constant value. */
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ REAL_VALUE_TYPE x;
+
+ x = TREE_REAL_CST (arg);
+ if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
+ {
+ tree type = TREE_TYPE (exp);
+ REAL_VALUE_TYPE r;
+
+ real_ceil (&r, TYPE_MODE (type), &x);
+ return build_real (type, r);
+ }
+ }
+
+ return fold_trunc_transparent_mathfn (exp);
+}
+
+/* Fold function call to builtin ffs, clz, ctz, popcount and parity
+ and their long and long long variants (i.e. ffsl and ffsll).
+ Return NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_bitop (tree exp)
+{
+ tree fndecl = get_callee_fndecl (exp);
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg;
+
+ if (! validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
+ return NULL_TREE;
+
+ /* Optimize for constant argument. */
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) == INTEGER_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ HOST_WIDE_INT hi, width, result;
+ unsigned HOST_WIDE_INT lo;
+ tree type, t;
+
+ type = TREE_TYPE (arg);
+ width = TYPE_PRECISION (type);
+ lo = TREE_INT_CST_LOW (arg);
+
+ /* Clear all the bits that are beyond the type's precision. */
+ if (width > HOST_BITS_PER_WIDE_INT)
+ {
+ hi = TREE_INT_CST_HIGH (arg);
+ if (width < 2 * HOST_BITS_PER_WIDE_INT)
+ hi &= ~((HOST_WIDE_INT) (-1) >> (width - HOST_BITS_PER_WIDE_INT));
+ }
+ else
+ {
+ hi = 0;
+ if (width < HOST_BITS_PER_WIDE_INT)
+ lo &= ~((unsigned HOST_WIDE_INT) (-1) << width);
+ }
+
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_FFS:
+ case BUILT_IN_FFSL:
+ case BUILT_IN_FFSLL:
+ if (lo != 0)
+ result = exact_log2 (lo & -lo) + 1;
+ else if (hi != 0)
+ result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi) + 1;
+ else
+ result = 0;
+ break;
+
+ case BUILT_IN_CLZ:
+ case BUILT_IN_CLZL:
+ case BUILT_IN_CLZLL:
+ if (hi != 0)
+ result = width - floor_log2 (hi) - 1 - HOST_BITS_PER_WIDE_INT;
+ else if (lo != 0)
+ result = width - floor_log2 (lo) - 1;
+ else if (! CLZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result))
+ result = width;
+ break;
+
+ case BUILT_IN_CTZ:
+ case BUILT_IN_CTZL:
+ case BUILT_IN_CTZLL:
+ if (lo != 0)
+ result = exact_log2 (lo & -lo);
+ else if (hi != 0)
+ result = HOST_BITS_PER_WIDE_INT + exact_log2 (hi & -hi);
+ else if (! CTZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (type), result))
+ result = width;
+ break;
+
+ case BUILT_IN_POPCOUNT:
+ case BUILT_IN_POPCOUNTL:
+ case BUILT_IN_POPCOUNTLL:
+ result = 0;
+ while (lo)
+ result++, lo &= lo - 1;
+ while (hi)
+ result++, hi &= hi - 1;
+ break;
+
+ case BUILT_IN_PARITY:
+ case BUILT_IN_PARITYL:
+ case BUILT_IN_PARITYLL:
+ result = 0;
+ while (lo)
+ result++, lo &= lo - 1;
+ while (hi)
+ result++, hi &= hi - 1;
+ result &= 1;
+ break;
+
+ default:
+ abort();
+ }
+
+ t = build_int_2 (result, 0);
+ TREE_TYPE (t) = TREE_TYPE (exp);
+ return t;
+ }
+
+ return NULL_TREE;
+}
+
/* Used by constant folding to eliminate some builtin calls early. EXP is
the CALL_EXPR of a call to a builtin function. */
tree
fold_builtin (tree exp)
{
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree fndecl = get_callee_fndecl (exp);
tree arglist = TREE_OPERAND (exp, 1);
tree type = TREE_TYPE (TREE_TYPE (fndecl));
build_real (type, dconst1),
arg0));
- /* Optimize pow(x,2.0) = x*x. */
- if (REAL_VALUES_EQUAL (c, dconst2)
- && (*lang_hooks.decls.global_bindings_p) () == 0
- && ! CONTAINS_PLACEHOLDER_P (arg0))
- {
- arg0 = save_expr (arg0);
- return fold (build (MULT_EXPR, type, arg0, arg0));
- }
-
- /* Optimize pow(x,-2.0) = 1.0/(x*x). */
- if (flag_unsafe_math_optimizations
- && REAL_VALUES_EQUAL (c, dconstm2)
- && (*lang_hooks.decls.global_bindings_p) () == 0
- && ! CONTAINS_PLACEHOLDER_P (arg0))
- {
- arg0 = save_expr (arg0);
- return fold (build (RDIV_EXPR, type,
- build_real (type, dconst1),
- fold (build (MULT_EXPR, type,
- arg0, arg0))));
- }
-
/* Optimize pow(x,0.5) = sqrt(x). */
if (flag_unsafe_math_optimizations
&& REAL_VALUES_EQUAL (c, dconsthalf))
case BUILT_IN_FLOOR:
case BUILT_IN_FLOORF:
case BUILT_IN_FLOORL:
+ return fold_builtin_floor (exp);
+
case BUILT_IN_CEIL:
case BUILT_IN_CEILF:
case BUILT_IN_CEILL:
+ return fold_builtin_ceil (exp);
+
case BUILT_IN_TRUNC:
case BUILT_IN_TRUNCF:
case BUILT_IN_TRUNCL:
+ return fold_builtin_trunc (exp);
+
case BUILT_IN_ROUND:
case BUILT_IN_ROUNDF:
case BUILT_IN_ROUNDL:
case BUILT_IN_NEARBYINTL:
return fold_trunc_transparent_mathfn (exp);
+ case BUILT_IN_FFS:
+ case BUILT_IN_FFSL:
+ case BUILT_IN_FFSLL:
+ case BUILT_IN_CLZ:
+ case BUILT_IN_CLZL:
+ case BUILT_IN_CLZLL:
+ case BUILT_IN_CTZ:
+ case BUILT_IN_CTZL:
+ case BUILT_IN_CTZLL:
+ case BUILT_IN_POPCOUNT:
+ case BUILT_IN_POPCOUNTL:
+ case BUILT_IN_POPCOUNTLL:
+ case BUILT_IN_PARITY:
+ case BUILT_IN_PARITYL:
+ case BUILT_IN_PARITYLL:
+ return fold_builtin_bitop (exp);
+
default:
break;
}
call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
call_expr, arglist);
- TREE_SIDE_EFFECTS (call_expr) = 1;
return fold (call_expr);
}