static void expand_builtin_return PARAMS ((rtx));
static enum type_class type_to_class PARAMS ((tree));
static rtx expand_builtin_classify_type PARAMS ((tree));
+static void expand_errno_check PARAMS ((tree, rtx));
static rtx expand_builtin_mathfn PARAMS ((tree, rtx, rtx));
+static rtx expand_builtin_mathfn_2 PARAMS ((tree, rtx, rtx));
static rtx expand_builtin_constant_p PARAMS ((tree));
static rtx expand_builtin_args_info PARAMS ((tree));
static rtx expand_builtin_next_arg PARAMS ((tree));
static rtx expand_builtin_strrchr PARAMS ((tree, rtx,
enum machine_mode));
static rtx expand_builtin_alloca PARAMS ((tree, rtx));
-static rtx expand_builtin_ffs PARAMS ((tree, rtx, rtx));
+static rtx expand_builtin_unop PARAMS ((tree, rtx, rtx, optab));
static rtx expand_builtin_frame_address PARAMS ((tree));
static rtx expand_builtin_fputs PARAMS ((tree, int, int));
static tree stabilize_va_list PARAMS ((tree, int));
static tree fold_builtin_inf PARAMS ((tree, int));
static tree fold_builtin_nan PARAMS ((tree, tree, int));
static int validate_arglist PARAMS ((tree, ...));
+static tree fold_trunc_transparent_mathfn PARAMS ((tree));
/* Return the alignment in bits of EXP, a pointer valued expression.
But don't return more than MAX_ALIGN no matter what.
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. */
+ current_function_calls_constant_p = 1;
+
tmp = expand_expr (arglist, NULL_RTX, VOIDmode, 0);
tmp = gen_rtx_CONSTANT_P_RTX (value_mode, tmp);
return tmp;
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_FLOOR:
case BUILT_IN_FLOORF:
case BUILT_IN_FLOORL:
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_FLOOR:
case BUILT_IN_FLOORF:
case BUILT_IN_FLOORL:
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_FLOOR:
case BUILT_IN_FLOORF:
case BUILT_IN_FLOORL:
return implicit_built_in_decls[fcode];
}
+/* If errno must be maintained, expand the RTL to check if the result,
+ TARGET, of a built-in function call, EXP, is NaN, and if so set
+ errno to EDOM. */
+
+static void
+expand_errno_check (exp, target)
+ tree exp;
+ rtx target;
+{
+ rtx lab;
+
+ if (flag_errno_math && HONOR_NANS (GET_MODE (target)))
+ {
+ lab = gen_label_rtx ();
+
+ /* Test the result; if it is NaN, set errno=EDOM because
+ the argument was not in the domain. */
+ emit_cmp_and_jump_insns (target, target, EQ, 0, GET_MODE (target),
+ 0, lab);
+
+#ifdef TARGET_EDOM
+ {
+#ifdef GEN_ERRNO_RTX
+ rtx errno_rtx = GEN_ERRNO_RTX;
+#else
+ rtx errno_rtx
+ = gen_rtx_MEM (word_mode, gen_rtx_SYMBOL_REF (Pmode, "errno"));
+#endif
+
+ emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM));
+ }
+#else
+ /* We can't set errno=EDOM directly; let the library call do it.
+ Pop the arguments right away in case the call gets deleted. */
+ NO_DEFER_POP;
+ expand_call (exp, target, 0);
+ OK_DEFER_POP;
+#endif
+
+ emit_label (lab);
+ }
+}
+
+
/* Expand a call to one of the builtin math functions (sin, cos, or sqrt).
Return 0 if a normal call should be emitted rather than expanding the
function in-line. EXP is the expression that is a call to the builtin
return 0;
}
- /* If errno must be maintained, we must set it to EDOM for NaN results. */
+ if (errno_set)
+ expand_errno_check (exp, target);
+
+ /* Output the entire sequence. */
+ insns = get_insns ();
+ end_sequence ();
+ emit_insn (insns);
+
+ return target;
+}
- if (flag_errno_math && errno_set && HONOR_NANS (argmode))
+/* Expand a call to the builtin binary math functions (pow and atan2).
+ Return 0 if a normal call should be emitted rather than expanding the
+ function in-line. EXP is the expression that is a call to the builtin
+ function; if convenient, the result should be placed in TARGET.
+ SUBTARGET may be used as the target for computing one of EXP's
+ operands. */
+
+static rtx
+expand_builtin_mathfn_2 (exp, target, subtarget)
+ tree exp;
+ rtx target, subtarget;
+{
+ optab builtin_optab;
+ rtx op0, op1, insns;
+ tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree arg0, arg1;
+ enum machine_mode argmode;
+ bool errno_set = true;
+ bool stable = true;
+
+ if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ arg0 = TREE_VALUE (arglist);
+ arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+
+ /* Stabilize the arguments. */
+ if (TREE_CODE (arg0) != VAR_DECL && TREE_CODE (arg0) != PARM_DECL)
+ {
+ arg0 = save_expr (arg0);
+ TREE_VALUE (arglist) = arg0;
+ stable = false;
+ }
+ if (TREE_CODE (arg1) != VAR_DECL && TREE_CODE (arg1) != PARM_DECL)
{
- rtx lab1;
+ arg1 = save_expr (arg1);
+ TREE_VALUE (TREE_CHAIN (arglist)) = arg1;
+ stable = false;
+ }
- lab1 = gen_label_rtx ();
+ if (! stable)
+ {
+ exp = copy_node (exp);
+ arglist = tree_cons (NULL_TREE, arg0,
+ build_tree_list (NULL_TREE, arg1));
+ TREE_OPERAND (exp, 1) = arglist;
+ }
- /* Test the result; if it is NaN, set errno=EDOM because
- the argument was not in the domain. */
- emit_cmp_and_jump_insns (target, target, EQ, 0, GET_MODE (target),
- 0, lab1);
+ op0 = expand_expr (arg0, subtarget, VOIDmode, 0);
+ op1 = expand_expr (arg1, 0, VOIDmode, 0);
-#ifdef TARGET_EDOM
- {
-#ifdef GEN_ERRNO_RTX
- rtx errno_rtx = GEN_ERRNO_RTX;
-#else
- rtx errno_rtx
- = gen_rtx_MEM (word_mode, gen_rtx_SYMBOL_REF (Pmode, "errno"));
-#endif
+ /* Make a suitable register to place result in. */
+ target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
- emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM));
- }
-#else
- /* We can't set errno=EDOM directly; let the library call do it.
- Pop the arguments right away in case the call gets deleted. */
- NO_DEFER_POP;
- expand_call (exp, target, 0);
- OK_DEFER_POP;
-#endif
+ emit_queue ();
+ start_sequence ();
+
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_POW:
+ case BUILT_IN_POWF:
+ case BUILT_IN_POWL:
+ builtin_optab = pow_optab; break;
+ case BUILT_IN_ATAN2:
+ case BUILT_IN_ATAN2F:
+ case BUILT_IN_ATAN2L:
+ builtin_optab = atan2_optab; break;
+ default:
+ abort ();
+ }
+
+ /* Compute into TARGET.
+ Set TARGET to wherever the result comes back. */
+ argmode = TYPE_MODE (TREE_TYPE (arg0));
+ target = expand_binop (argmode, builtin_optab, op0, op1,
+ target, 0, OPTAB_DIRECT);
- emit_label (lab1);
+ /* If we were unable to expand via the builtin, stop the
+ sequence (without outputting the insns) and return 0, causing
+ a call to the library function. */
+ if (target == 0)
+ {
+ end_sequence ();
+ return 0;
}
+ if (errno_set)
+ expand_errno_check (exp, target);
+
/* Output the entire sequence. */
insns = get_insns ();
end_sequence ();
return result;
}
-/* Expand a call to the ffs builtin. The arguments are in ARGLIST.
+/* Expand a call to a unary builtin. The arguments are in ARGLIST.
Return 0 if a normal call should be emitted rather than expanding the
function in-line. 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_ffs (arglist, target, subtarget)
+expand_builtin_unop (arglist, target, subtarget, op_optab)
tree arglist;
rtx target, subtarget;
+ optab op_optab;
{
rtx op0;
if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
/* Compute the argument. */
op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0);
- /* Compute ffs, into TARGET if possible.
+ /* Compute op, into TARGET if possible.
Set TARGET to wherever the result comes back. */
target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))),
- ffs_optab, op0, target, 1);
+ op_optab, op0, target, 1);
if (target == 0)
abort ();
return target;
target = expand_expr (exp, target, VOIDmode, EXPAND_NORMAL);
/* Don't bother with expected value notes for integral constants. */
- if (GET_CODE (target) != CONST_INT)
+ if (flag_guess_branch_prob && GET_CODE (target) != CONST_INT)
{
/* We do need to force this into a register so that we can be
moderately sure to be able to correctly interpret the branch
tree arglist = TREE_OPERAND (exp, 1);
enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+ /* Perform postincrements before expanding builtin functions. */
+ emit_queue ();
+
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
return (*targetm.expand_builtin) (exp, target, subtarget, mode, ignore);
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_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:
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:
+ if (! flag_unsafe_math_optimizations)
+ break;
+ target = expand_builtin_mathfn_2 (exp, target, subtarget);
+ if (target)
+ return target;
+ break;
+
case BUILT_IN_APPLY_ARGS:
return expand_builtin_apply_args ();
break;
case BUILT_IN_FFS:
- target = expand_builtin_ffs (arglist, target, subtarget);
+ case BUILT_IN_FFSL:
+ case BUILT_IN_FFSLL:
+ target = expand_builtin_unop (arglist, target, subtarget, ffs_optab);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_CLZ:
+ case BUILT_IN_CLZL:
+ case BUILT_IN_CLZLL:
+ target = expand_builtin_unop (arglist, target, subtarget, clz_optab);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_CTZ:
+ case BUILT_IN_CTZL:
+ case BUILT_IN_CTZLL:
+ target = expand_builtin_unop (arglist, target, subtarget, ctz_optab);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_POPCOUNT:
+ case BUILT_IN_POPCOUNTL:
+ case BUILT_IN_POPCOUNTLL:
+ target = expand_builtin_unop (arglist, target, subtarget,
+ popcount_optab);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_PARITY:
+ case BUILT_IN_PARITYL:
+ case BUILT_IN_PARITYLL:
+ target = expand_builtin_unop (arglist, target, subtarget, parity_optab);
if (target)
return target;
break;
arglist = TREE_OPERAND (t, 1);
if (! arglist
- || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE
- || TREE_CHAIN (arglist))
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE)
return END_BUILTINS;
+ arglist = TREE_CHAIN (arglist);
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_POW:
+ case BUILT_IN_POWF:
+ case BUILT_IN_POWL:
+ case BUILT_IN_ATAN2:
+ case BUILT_IN_ATAN2F:
+ case BUILT_IN_ATAN2L:
+ if (! arglist
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE
+ || TREE_CHAIN (arglist))
+ return END_BUILTINS;
+ break;
+
+ default:
+ if (arglist)
+ return END_BUILTINS;
+ break;
+ }
+
return DECL_FUNCTION_CODE (fndecl);
}
return build_real (type, real);
}
+/* EXP is assumed to me 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 (exp)
+ tree exp;
+{
+ tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree arglist = TREE_OPERAND (exp, 1);
+ enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+
+ if (optimize && validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ tree arg0 = strip_float_extensions (TREE_VALUE (arglist));
+ tree ftype = TREE_TYPE (exp);
+ tree newtype = TREE_TYPE (arg0);
+ tree decl;
+
+ if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype)
+ && (decl = mathfn_built_in (newtype, fcode)))
+ {
+ arglist =
+ build_tree_list (NULL_TREE, fold (convert (newtype, arg0)));
+ return convert (ftype,
+ build_function_call_expr (decl, arglist));
+ }
+ }
+ return 0;
+}
+
/* Used by constant folding to eliminate some builtin calls early. EXP is
the CALL_EXPR of a call to a builtin function. */
{
tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
tree arglist = TREE_OPERAND (exp, 1);
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
REAL_VALUE_TYPE r, x;
x = TREE_REAL_CST (arg);
- mode = TYPE_MODE (TREE_TYPE (arg));
+ mode = TYPE_MODE (type);
if (real_sqrt (&r, mode, &x)
|| (!flag_trapping_math && !flag_errno_math))
- return build_real (TREE_TYPE (arg), r);
+ return build_real (type, r);
}
/* Optimize sqrt(exp(x)) = exp(x/2.0). */
|| fcode == BUILT_IN_EXPL))
{
tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
- arg = build (RDIV_EXPR, TREE_TYPE (arg),
+ arg = build (RDIV_EXPR, type,
TREE_VALUE (TREE_OPERAND (arg, 1)),
- build_real (TREE_TYPE (arg), dconst2));
+ build_real (type, dconst2));
arglist = build_tree_list (NULL_TREE, arg);
return build_function_call_expr (expfn, arglist);
}
/* Optimize exp(0.0) = 1.0. */
if (real_zerop (arg))
- return build_real (TREE_TYPE (arg), dconst1);
+ return build_real (type, dconst1);
/* Optimize exp(log(x)) = x. */
fcode = builtin_mathfn_code (arg);
/* Optimize log(1.0) = 0.0. */
if (real_onep (arg))
- return build_real (TREE_TYPE (arg), dconst0);
+ return build_real (type, dconst0);
/* Optimize log(exp(x)) = x. */
fcode = builtin_mathfn_code (arg);
{
tree logfn = build_function_call_expr (fndecl,
TREE_OPERAND (arg, 1));
- return fold (build (RDIV_EXPR, TREE_TYPE (arg), logfn,
- build_real (TREE_TYPE (arg), dconst2)));
+ return fold (build (RDIV_EXPR, type, logfn,
+ build_real (type, dconst2)));
}
}
break;
+ case BUILT_IN_POW:
+ case BUILT_IN_POWF:
+ case BUILT_IN_POWL:
+ if (validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+ {
+ tree arg0 = TREE_VALUE (arglist);
+ tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+
+ /* Optimize pow(x,0.0) = 1.0. */
+ if (real_zerop (arg1))
+ return omit_one_operand (type, build_real (type, dconst1), arg0);
+
+ /* Optimize pow(1.0,y) = 1.0. */
+ if (real_onep (arg0))
+ return omit_one_operand (type, build_real (type, dconst1), arg1);
+ }
+ break;
+
case BUILT_IN_INF:
case BUILT_IN_INFF:
case BUILT_IN_INFL:
- return fold_builtin_inf (TREE_TYPE (TREE_TYPE (fndecl)), true);
+ return fold_builtin_inf (type, true);
case BUILT_IN_HUGE_VAL:
case BUILT_IN_HUGE_VALF:
case BUILT_IN_HUGE_VALL:
- return fold_builtin_inf (TREE_TYPE (TREE_TYPE (fndecl)), false);
+ return fold_builtin_inf (type, false);
case BUILT_IN_NAN:
case BUILT_IN_NANF:
case BUILT_IN_NANL:
- return fold_builtin_nan (arglist, TREE_TYPE (TREE_TYPE (fndecl)), true);
+ return fold_builtin_nan (arglist, type, true);
case BUILT_IN_NANS:
case BUILT_IN_NANSF:
case BUILT_IN_NANSL:
- return fold_builtin_nan (arglist, TREE_TYPE (TREE_TYPE (fndecl)), false);
+ return fold_builtin_nan (arglist, type, false);
+
+ 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 fold_trunc_transparent_mathfn (exp);
default:
break;
void
purge_builtin_constant_p ()
{
- rtx insn, done, set;
- rtx arg, new, note;
- basic_block bb;
-
- FOR_EACH_BB (bb)
- {
- done = NEXT_INSN (bb->end);
- for (insn = bb->head; insn != done; insn = NEXT_INSN (insn))
- if (INSN_P (insn)
- && (set = single_set (insn)) != NULL_RTX
- && GET_CODE (SET_SRC (set)) == CONSTANT_P_RTX)
- {
- arg = XEXP (SET_SRC (set), 0);
- new = CONSTANT_P (arg) ? const1_rtx : const0_rtx;
- validate_change (insn, &SET_SRC (set), new, 0);
+ rtx insn, set, arg, new, note;
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (INSN_P (insn)
+ && (set = single_set (insn)) != NULL_RTX
+ && (GET_CODE (arg = SET_SRC (set)) == CONSTANT_P_RTX
+ || (GET_CODE (arg) == SUBREG
+ && (GET_CODE (arg = SUBREG_REG (arg))
+ == CONSTANT_P_RTX))))
+ {
+ arg = XEXP (arg, 0);
+ new = CONSTANT_P (arg) ? const1_rtx : const0_rtx;
+ validate_change (insn, &SET_SRC (set), new, 0);
- /* Remove the REG_EQUAL note from the insn. */
- if ((note = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0)
- remove_note (insn, note);
- }
- }
+ /* Remove the REG_EQUAL note from the insn. */
+ if ((note = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0)
+ remove_note (insn, note);
+ }
}