/* Expand builtin functions.
Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
This file is part of GCC.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
#include "config.h"
#include "system.h"
#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 const char *c_getstr (tree);
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 rtx get_memory_rtx (tree, tree);
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_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_sincos (tree);
+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_strcpy (tree, rtx, enum machine_mode);
+static rtx expand_builtin_memmove (tree, tree, rtx, enum machine_mode, tree);
+static rtx expand_builtin_bcopy (tree);
+static rtx expand_builtin_strcpy (tree, 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, tree, rtx, enum machine_mode);
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, 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_abs (tree, tree);
static tree fold_builtin_unordered_cmp (tree, tree, enum tree_code,
enum tree_code);
-static tree fold_builtin_1 (tree, bool);
+static tree fold_builtin_1 (tree, tree, bool);
static tree fold_builtin_strpbrk (tree, tree);
static tree fold_builtin_strstr (tree, tree);
static tree fold_builtin_strcspn (tree);
static tree fold_builtin_sprintf (tree, int);
+static rtx expand_builtin_object_size (tree);
+static rtx expand_builtin_memory_chk (tree, rtx, enum machine_mode,
+ enum built_in_function);
+static void maybe_emit_chk_warning (tree, enum built_in_function);
+static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
+static tree fold_builtin_object_size (tree);
+static tree fold_builtin_strcat_chk (tree, tree);
+static tree fold_builtin_strncat_chk (tree, tree);
+static tree fold_builtin_sprintf_chk (tree, enum built_in_function);
+static tree fold_builtin_printf (tree, tree, bool, enum built_in_function);
+static tree fold_builtin_fprintf (tree, tree, bool, enum built_in_function);
+static bool init_target_chars (void);
+
+static unsigned HOST_WIDE_INT target_newline;
+static unsigned HOST_WIDE_INT target_percent;
+static unsigned HOST_WIDE_INT target_c;
+static unsigned HOST_WIDE_INT target_s;
+static char target_percent_c[3];
+static char target_percent_s[3];
+static char target_percent_s_newline[4];
+
+/* 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;
}
}
/* Cast a target constant CST to target CHAR and if that value fits into
- host char type, return zero and put that value into variable pointed by
+ host char type, return zero and put that value into variable pointed to by
P. */
static int
#ifdef INITIAL_FRAME_ADDRESS_RTX
rtx tem = INITIAL_FRAME_ADDRESS_RTX;
#else
- rtx tem = hard_frame_pointer_rtx;
+ rtx tem;
+
+ /* For a zero count, we don't care what frame address we return, so frame
+ pointer elimination is OK, and using the soft frame pointer is OK.
+ For a nonzero count, we require a stable offset from the current frame
+ pointer to the previous one, so we must use the hard frame pointer, and
+ we must disable frame pointer elimination. */
+ if (count == 0)
+ tem = frame_pointer_rtx;
+ else
+ {
+ tem = hard_frame_pointer_rtx;
+
+ /* Tell reload not to eliminate the frame pointer. */
+ current_function_accesses_prior_frames = 1;
+ }
#endif
/* Some machines need special handling before we can access
tem = DYNAMIC_CHAIN_ADDRESS (tem);
#endif
tem = memory_address (Pmode, tem);
- tem = gen_rtx_MEM (Pmode, tem);
- set_mem_alias_set (tem, get_frame_alias_set ());
+ tem = gen_frame_mem (Pmode, tem);
tem = copy_to_reg (tem);
}
#else
tem = memory_address (Pmode,
plus_constant (tem, GET_MODE_SIZE (Pmode)));
- tem = gen_rtx_MEM (Pmode, tem);
- set_mem_alias_set (tem, get_frame_alias_set ());
+ tem = gen_frame_mem (Pmode, tem);
#endif
return tem;
}
|| REGNO (target) < FIRST_PSEUDO_REGISTER)
target = gen_reg_rtx (TYPE_MODE (integer_type_node));
- buf_addr = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0);
+ buf_addr = expand_normal (TREE_VALUE (arglist));
next_lab = gen_label_rtx ();
cont_lab = gen_label_rtx ();
arglist = TREE_CHAIN (arglist);
t_save_area = TREE_VALUE (arglist);
- r_label = expand_expr (t_label, NULL_RTX, VOIDmode, 0);
+ r_label = expand_normal (t_label);
r_label = convert_memory_address (Pmode, r_label);
- r_save_area = expand_expr (t_save_area, NULL_RTX, VOIDmode, 0);
+ r_save_area = expand_normal (t_save_area);
r_save_area = convert_memory_address (Pmode, r_save_area);
r_fp = gen_rtx_MEM (Pmode, r_save_area);
r_sp = gen_rtx_MEM (STACK_SAVEAREA_MODE (SAVE_NONLOCAL),
error ("second argument to %<__builtin_prefetch%> must be a constant");
arg1 = integer_zero_node;
}
- op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
+ op1 = expand_normal (arg1);
/* 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;
}
error ("third argument to %<__builtin_prefetch%> must be a constant");
arg2 = integer_zero_node;
}
- op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0);
+ op2 = expand_normal (arg2);
/* 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;
}
}
/* Get a MEM rtx for expression EXP which is the address of an operand
- to be used to be used in a string instruction (cmpstrsi, movmemsi, ..). */
+ to be used in a string instruction (cmpstrsi, movmemsi, ..). LEN is
+ the maximum length of the block of memory that might be accessed or
+ NULL if unknown. */
static rtx
-get_memory_rtx (tree exp)
+get_memory_rtx (tree exp, tree len)
{
- 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
if (exp)
{
set_mem_attributes (mem, exp, 0);
+
+ /* Allow the string and memory builtins to overflow from one
+ field into another, see http://gcc.gnu.org/PR23561.
+ Thus avoid COMPONENT_REFs in MEM_EXPR unless we know the whole
+ memory accessed by the string or memory builtin will fit
+ within the field. */
+ if (MEM_EXPR (mem) && TREE_CODE (MEM_EXPR (mem)) == COMPONENT_REF)
+ {
+ tree mem_expr = MEM_EXPR (mem);
+ HOST_WIDE_INT offset = -1, length = -1;
+ tree inner = exp;
+
+ while (TREE_CODE (inner) == ARRAY_REF
+ || TREE_CODE (inner) == NOP_EXPR
+ || TREE_CODE (inner) == CONVERT_EXPR
+ || TREE_CODE (inner) == NON_LVALUE_EXPR
+ || TREE_CODE (inner) == VIEW_CONVERT_EXPR
+ || TREE_CODE (inner) == SAVE_EXPR)
+ inner = TREE_OPERAND (inner, 0);
+
+ gcc_assert (TREE_CODE (inner) == COMPONENT_REF);
+
+ if (MEM_OFFSET (mem)
+ && GET_CODE (MEM_OFFSET (mem)) == CONST_INT)
+ offset = INTVAL (MEM_OFFSET (mem));
+
+ if (offset >= 0 && len && host_integerp (len, 0))
+ length = tree_low_cst (len, 0);
+
+ while (TREE_CODE (inner) == COMPONENT_REF)
+ {
+ tree field = TREE_OPERAND (inner, 1);
+ gcc_assert (! DECL_BIT_FIELD (field));
+ gcc_assert (TREE_CODE (mem_expr) == COMPONENT_REF);
+ gcc_assert (field == TREE_OPERAND (mem_expr, 1));
+
+ if (length >= 0
+ && TYPE_SIZE_UNIT (TREE_TYPE (inner))
+ && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (inner)), 0))
+ {
+ HOST_WIDE_INT size
+ = tree_low_cst (TYPE_SIZE_UNIT (TREE_TYPE (inner)), 0);
+ /* If we can prove the memory starting at XEXP (mem, 0)
+ and ending at XEXP (mem, 0) + LENGTH will fit into
+ this field, we can keep that COMPONENT_REF in MEM_EXPR. */
+ if (offset <= size
+ && length <= size
+ && offset + length <= size)
+ break;
+ }
+
+ if (offset >= 0
+ && host_integerp (DECL_FIELD_OFFSET (field), 0))
+ offset += tree_low_cst (DECL_FIELD_OFFSET (field), 0)
+ + tree_low_cst (DECL_FIELD_BIT_OFFSET (field), 1)
+ / BITS_PER_UNIT;
+ else
+ {
+ offset = -1;
+ length = -1;
+ }
+
+ mem_expr = TREE_OPERAND (mem_expr, 0);
+ inner = TREE_OPERAND (inner, 0);
+ }
+
+ if (mem_expr == NULL)
+ offset = -1;
+ if (mem_expr != MEM_EXPR (mem))
+ {
+ set_mem_expr (mem, mem_expr);
+ set_mem_offset (mem, offset >= 0 ? GEN_INT (offset) : NULL_RTX);
+ }
+ }
set_mem_alias_set (mem, 0);
set_mem_size (mem, NULL_RTX);
}
{
case VOID_TYPE: return void_type_class;
case INTEGER_TYPE: return integer_type_class;
- case CHAR_TYPE: return char_type_class;
case ENUMERAL_TYPE: return enumeral_type_class;
case BOOLEAN_TYPE: return boolean_type_class;
case POINTER_TYPE: return pointer_type_class;
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)
switch (DECL_FUNCTION_CODE (fndecl))
{
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
+ CASE_FLT_FN (BUILT_IN_SQRT):
errno_set = ! tree_expr_nonnegative_p (arg);
builtin_optab = sqrt_optab;
break;
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
+ CASE_FLT_FN (BUILT_IN_EXP):
errno_set = true; builtin_optab = exp_optab; break;
- case BUILT_IN_EXP10:
- case BUILT_IN_EXP10F:
- case BUILT_IN_EXP10L:
- case BUILT_IN_POW10:
- case BUILT_IN_POW10F:
- case BUILT_IN_POW10L:
+ CASE_FLT_FN (BUILT_IN_EXP10):
+ CASE_FLT_FN (BUILT_IN_POW10):
errno_set = true; builtin_optab = exp10_optab; break;
- case BUILT_IN_EXP2:
- case BUILT_IN_EXP2F:
- case BUILT_IN_EXP2L:
+ CASE_FLT_FN (BUILT_IN_EXP2):
errno_set = true; builtin_optab = exp2_optab; break;
- case BUILT_IN_EXPM1:
- case BUILT_IN_EXPM1F:
- case BUILT_IN_EXPM1L:
+ CASE_FLT_FN (BUILT_IN_EXPM1):
errno_set = true; builtin_optab = expm1_optab; break;
- case BUILT_IN_LOGB:
- case BUILT_IN_LOGBF:
- case BUILT_IN_LOGBL:
+ CASE_FLT_FN (BUILT_IN_LOGB):
errno_set = true; builtin_optab = logb_optab; break;
- case BUILT_IN_ILOGB:
- case BUILT_IN_ILOGBF:
- case BUILT_IN_ILOGBL:
+ CASE_FLT_FN (BUILT_IN_ILOGB):
errno_set = true; builtin_optab = ilogb_optab; break;
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
+ CASE_FLT_FN (BUILT_IN_LOG):
errno_set = true; builtin_optab = log_optab; break;
- case BUILT_IN_LOG10:
- case BUILT_IN_LOG10F:
- case BUILT_IN_LOG10L:
+ CASE_FLT_FN (BUILT_IN_LOG10):
errno_set = true; builtin_optab = log10_optab; break;
- case BUILT_IN_LOG2:
- case BUILT_IN_LOG2F:
- case BUILT_IN_LOG2L:
+ CASE_FLT_FN (BUILT_IN_LOG2):
errno_set = true; builtin_optab = log2_optab; break;
- case BUILT_IN_LOG1P:
- case BUILT_IN_LOG1PF:
- case BUILT_IN_LOG1PL:
+ CASE_FLT_FN (BUILT_IN_LOG1P):
errno_set = true; builtin_optab = log1p_optab; break;
- case BUILT_IN_ASIN:
- case BUILT_IN_ASINF:
- case BUILT_IN_ASINL:
+ CASE_FLT_FN (BUILT_IN_ASIN):
builtin_optab = asin_optab; break;
- case BUILT_IN_ACOS:
- case BUILT_IN_ACOSF:
- case BUILT_IN_ACOSL:
+ CASE_FLT_FN (BUILT_IN_ACOS):
builtin_optab = acos_optab; break;
- case BUILT_IN_TAN:
- case BUILT_IN_TANF:
- case BUILT_IN_TANL:
+ CASE_FLT_FN (BUILT_IN_TAN):
builtin_optab = tan_optab; break;
- case BUILT_IN_ATAN:
- case BUILT_IN_ATANF:
- case BUILT_IN_ATANL:
+ CASE_FLT_FN (BUILT_IN_ATAN):
builtin_optab = atan_optab; break;
- case BUILT_IN_FLOOR:
- case BUILT_IN_FLOORF:
- case BUILT_IN_FLOORL:
+ CASE_FLT_FN (BUILT_IN_FLOOR):
builtin_optab = floor_optab; break;
- case BUILT_IN_CEIL:
- case BUILT_IN_CEILF:
- case BUILT_IN_CEILL:
+ CASE_FLT_FN (BUILT_IN_CEIL):
builtin_optab = ceil_optab; break;
- case BUILT_IN_TRUNC:
- case BUILT_IN_TRUNCF:
- case BUILT_IN_TRUNCL:
+ CASE_FLT_FN (BUILT_IN_TRUNC):
builtin_optab = btrunc_optab; break;
- case BUILT_IN_ROUND:
- case BUILT_IN_ROUNDF:
- case BUILT_IN_ROUNDL:
+ CASE_FLT_FN (BUILT_IN_ROUND):
builtin_optab = round_optab; break;
- case BUILT_IN_NEARBYINT:
- case BUILT_IN_NEARBYINTF:
- case BUILT_IN_NEARBYINTL:
+ CASE_FLT_FN (BUILT_IN_NEARBYINT):
builtin_optab = nearbyint_optab; break;
- case BUILT_IN_RINT:
- case BUILT_IN_RINTF:
- case BUILT_IN_RINTL:
+ CASE_FLT_FN (BUILT_IN_RINT):
builtin_optab = rint_optab; break;
+ CASE_FLT_FN (BUILT_IN_LRINT):
+ CASE_FLT_FN (BUILT_IN_LLRINT):
+ builtin_optab = lrint_optab; break;
default:
gcc_unreachable ();
}
switch (DECL_FUNCTION_CODE (fndecl))
{
- case BUILT_IN_POW:
- case BUILT_IN_POWF:
- case BUILT_IN_POWL:
+ CASE_FLT_FN (BUILT_IN_POW):
builtin_optab = pow_optab; break;
- case BUILT_IN_ATAN2:
- case BUILT_IN_ATAN2F:
- case BUILT_IN_ATAN2L:
+ CASE_FLT_FN (BUILT_IN_ATAN2):
builtin_optab = atan2_optab; break;
- case BUILT_IN_LDEXP:
- case BUILT_IN_LDEXPF:
- case BUILT_IN_LDEXPL:
+ CASE_FLT_FN (BUILT_IN_LDEXP):
builtin_optab = ldexp_optab; break;
- case BUILT_IN_FMOD:
- case BUILT_IN_FMODF:
- case BUILT_IN_FMODL:
+ CASE_FLT_FN (BUILT_IN_FMOD):
builtin_optab = fmod_optab; break;
- case BUILT_IN_DREM:
- case BUILT_IN_DREMF:
- case BUILT_IN_DREML:
+ CASE_FLT_FN (BUILT_IN_DREM):
builtin_optab = drem_optab; break;
default:
gcc_unreachable ();
if (! stable)
exp = build_function_call_expr (fndecl, arglist);
- op0 = expand_expr (arg0, subtarget, VOIDmode, 0);
- op1 = expand_expr (arg1, 0, VOIDmode, 0);
+ op0 = expand_expr (arg0, subtarget, VOIDmode, EXPAND_NORMAL);
+ op1 = expand_normal (arg1);
start_sequence ();
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;
- bool errno_set = false;
tree arg, narg;
if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
switch (DECL_FUNCTION_CODE (fndecl))
{
- 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_FLT_FN (BUILT_IN_SIN):
+ CASE_FLT_FN (BUILT_IN_COS):
builtin_optab = sincos_optab; break;
default:
gcc_unreachable ();
/* Make a suitable register to place result in. */
mode = TYPE_MODE (TREE_TYPE (exp));
- if (! flag_errno_math || ! HONOR_NANS (mode))
- errno_set = false;
-
/* Check if sincos insn is available, otherwise fallback
to sin or cos insn. */
if (builtin_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing) {
switch (DECL_FUNCTION_CODE (fndecl))
{
- case BUILT_IN_SIN:
- case BUILT_IN_SINF:
- case BUILT_IN_SINL:
+ CASE_FLT_FN (BUILT_IN_SIN):
builtin_optab = sin_optab; break;
- case BUILT_IN_COS:
- case BUILT_IN_COSF:
- case BUILT_IN_COSL:
+ CASE_FLT_FN (BUILT_IN_COS):
builtin_optab = cos_optab; break;
default:
gcc_unreachable ();
switch (DECL_FUNCTION_CODE (fndecl))
{
- case BUILT_IN_SIN:
- case BUILT_IN_SINF:
- case BUILT_IN_SINL:
+ CASE_FLT_FN (BUILT_IN_SIN):
result = expand_twoval_unop (builtin_optab, op0, 0, target, 0);
break;
- case BUILT_IN_COS:
- case BUILT_IN_COSF:
- case BUILT_IN_COSL:
+ CASE_FLT_FN (BUILT_IN_COS):
result = expand_twoval_unop (builtin_optab, op0, target, 0, 0);
break;
default:
if (target != 0)
{
- if (errno_set)
- expand_errno_check (exp, target);
-
/* Output the entire sequence. */
insns = get_insns ();
end_sequence ();
end_sequence ();
}
- before_call = get_last_insn ();
-
target = expand_call (exp, target, target == const0_rtx);
return target;
}
+/* Expand a call to the builtin sincos math function.
+ Return 0 if a normal call should be emitted rather than expanding the
+ function in-line. EXP is the expression that is a call to the builtin
+ function. */
+
+static rtx
+expand_builtin_sincos (tree exp)
+{
+ rtx op0, op1, op2, target1, target2;
+ tree arglist = TREE_OPERAND (exp, 1);
+ enum machine_mode mode;
+ tree arg, sinp, cosp;
+ int result;
+
+ if (!validate_arglist (arglist, REAL_TYPE,
+ POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
+ return 0;
+
+ arg = TREE_VALUE (arglist);
+ sinp = TREE_VALUE (TREE_CHAIN (arglist));
+ cosp = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+ /* Make a suitable register to place result in. */
+ mode = TYPE_MODE (TREE_TYPE (arg));
+
+ /* Check if sincos insn is available, otherwise emit the call. */
+ if (sincos_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+ return NULL_RTX;
+
+ target1 = gen_reg_rtx (mode);
+ target2 = gen_reg_rtx (mode);
+
+ op0 = expand_normal (arg);
+ op1 = expand_normal (build_fold_indirect_ref (sinp));
+ op2 = expand_normal (build_fold_indirect_ref (cosp));
+
+ /* Compute into target1 and target2.
+ Set TARGET to wherever the result comes back. */
+ result = expand_twoval_unop (sincos_optab, op0, target2, target1, 0);
+ gcc_assert (result);
+
+ /* Move target1 and target2 to the memory locations indicated
+ by op1 and op2. */
+ emit_move_insn (op1, target1);
+ emit_move_insn (op2, target2);
+
+ return const0_rtx;
+}
+
+/* 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_FLT_FN (BUILT_IN_LCEIL):
+ CASE_FLT_FN (BUILT_IN_LLCEIL):
+ builtin_optab = lceil_optab;
+ fallback_fn = BUILT_IN_CEIL;
+ break;
+
+ CASE_FLT_FN (BUILT_IN_LFLOOR):
+ CASE_FLT_FN (BUILT_IN_LLFLOOR):
+ 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;
}
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);
if (src_align == 0)
return 0;
- dest_mem = get_memory_rtx (dest);
+ dest_mem = get_memory_rtx (dest, len);
set_mem_align (dest_mem, dest_align);
- len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+ len_rtx = expand_normal (len);
src_str = c_getstr (src);
/* If SRC is a string constant and block move would be done
return dest_mem;
}
- src_mem = get_memory_rtx (src);
+ src_mem = get_memory_rtx (src, len);
set_mem_align (src_mem, src_align);
/* 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)
{
}
/* Expand a call to the mempcpy builtin, with arguments in ARGLIST.
- Return 0 if we failed the caller should emit a normal call,
+ Return 0 if we failed; the caller should emit a normal call,
otherwise try to get the result in TARGET, if convenient (and in
mode MODE if that's convenient). If ENDP is 0 return the
destination pointer, if ENDP is 1 return the end pointer ala
if (! host_integerp (len, 1))
return 0;
- len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+ len_rtx = expand_normal (len);
src_str = c_getstr (src);
/* If SRC is a string constant and block move would be done
&& can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str,
(void *) src_str, dest_align))
{
- dest_mem = get_memory_rtx (dest);
+ dest_mem = get_memory_rtx (dest, len);
set_mem_align (dest_mem, dest_align);
dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx),
builtin_memcpy_read_str,
&& can_move_by_pieces (INTVAL (len_rtx),
MIN (dest_align, src_align)))
{
- dest_mem = get_memory_rtx (dest);
+ dest_mem = get_memory_rtx (dest, len);
set_mem_align (dest_mem, dest_align);
- src_mem = get_memory_rtx (src);
+ src_mem = get_memory_rtx (src, len);
set_mem_align (src_mem, src_align);
dest_mem = move_by_pieces (dest_mem, src_mem, INTVAL (len_rtx),
MIN (dest_align, src_align), endp);
}
/* Expand expression EXP, which is a call to the memmove builtin. Return 0
- if we failed the caller should emit a normal call. */
+ if we failed; the caller should emit a normal call. */
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
if (!HAVE_movstr)
return 0;
- dest_mem = get_memory_rtx (dest);
- src_mem = get_memory_rtx (src);
+ dest_mem = get_memory_rtx (dest, NULL);
+ src_mem = get_memory_rtx (src, NULL);
if (!endp)
{
target = force_reg (Pmode, XEXP (dest_mem, 0));
convenient). */
static rtx
-expand_builtin_strcpy (tree exp, rtx target, enum machine_mode mode)
+expand_builtin_strcpy (tree fndecl, tree arglist, rtx target, enum machine_mode mode)
{
- 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);
if (TREE_CODE (len) == INTEGER_CST)
{
- rtx len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+ rtx len_rtx = expand_normal (len);
if (GET_CODE (len_rtx) == CONST_INT)
{
- ret = expand_builtin_strcpy (exp, target, mode);
+ ret = expand_builtin_strcpy (get_callee_fndecl (exp),
+ arglist, target, mode);
if (ret)
{
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);
(void *) p, dest_align))
return 0;
- dest_mem = get_memory_rtx (dest);
+ dest_mem = get_memory_rtx (dest, len);
store_by_pieces (dest_mem, tree_low_cst (len, 1),
builtin_strncpy_read_str,
(void *) p, dest_align, 0);
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))
return expand_expr (dest, target, mode, EXPAND_NORMAL);
}
+ len_rtx = expand_normal (len);
+ dest_mem = get_memory_rtx (dest, len);
+
if (TREE_CODE (val) != INTEGER_CST)
{
rtx val_rtx;
- if (!host_integerp (len, 1))
- return 0;
-
- if (optimize_size && tree_low_cst (len, 1) > 1)
- return 0;
+ val = fold_build1 (CONVERT_EXPR, unsigned_char_type_node, val);
+ val_rtx = expand_normal (val);
/* Assume that we can memset by pieces if we can store the
* the coefficients by pieces (in the required modes).
* We can't pass builtin_memset_gen_str as that emits RTL. */
c = 1;
- if (!can_store_by_pieces (tree_low_cst (len, 1),
- builtin_memset_read_str,
- &c, dest_align))
+ if (host_integerp (len, 1)
+ && !(optimize_size && tree_low_cst (len, 1) > 1)
+ && can_store_by_pieces (tree_low_cst (len, 1),
+ builtin_memset_read_str, &c, dest_align))
+ {
+ val_rtx = force_reg (TYPE_MODE (unsigned_char_type_node),
+ val_rtx);
+ store_by_pieces (dest_mem, tree_low_cst (len, 1),
+ builtin_memset_gen_str, val_rtx, dest_align, 0);
+ }
+ else if (!set_storage_via_setmem(dest_mem, len_rtx, val_rtx,
+ dest_align))
return 0;
- val = fold (build1 (CONVERT_EXPR, unsigned_char_type_node, val));
- val_rtx = expand_expr (val, NULL_RTX, VOIDmode, 0);
- val_rtx = force_reg (TYPE_MODE (unsigned_char_type_node),
- val_rtx);
- dest_mem = get_memory_rtx (dest);
- store_by_pieces (dest_mem, tree_low_cst (len, 1),
- builtin_memset_gen_str,
- val_rtx, dest_align, 0);
dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
dest_mem = convert_memory_address (ptr_mode, dest_mem);
return dest_mem;
if (c)
{
- if (!host_integerp (len, 1))
- return 0;
- if (!can_store_by_pieces (tree_low_cst (len, 1),
- builtin_memset_read_str, &c,
- dest_align))
+ if (host_integerp (len, 1)
+ && !(optimize_size && tree_low_cst (len, 1) > 1)
+ && can_store_by_pieces (tree_low_cst (len, 1),
+ builtin_memset_read_str, &c, dest_align))
+ store_by_pieces (dest_mem, tree_low_cst (len, 1),
+ builtin_memset_read_str, &c, dest_align, 0);
+ else if (!set_storage_via_setmem (dest_mem, len_rtx, GEN_INT (c),
+ dest_align))
return 0;
- dest_mem = get_memory_rtx (dest);
- store_by_pieces (dest_mem, tree_low_cst (len, 1),
- builtin_memset_read_str,
- &c, dest_align, 0);
dest_mem = force_operand (XEXP (dest_mem, 0), NULL_RTX);
dest_mem = convert_memory_address (ptr_mode, dest_mem);
return dest_mem;
}
- len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
-
- 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.
return expand_expr (result, target, mode, EXPAND_NORMAL);
}
-#if defined HAVE_cmpmemsi || defined HAVE_cmpstrsi
+#if defined HAVE_cmpmemsi || defined HAVE_cmpstrnsi
{
tree arg1 = TREE_VALUE (arglist);
tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
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;
+#ifdef HAVE_cmpstrnsi
+ if (HAVE_cmpstrnsi)
+ insn_mode = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode;
else
#endif
return 0;
&& REGNO (result) >= FIRST_PSEUDO_REGISTER))
result = gen_reg_rtx (insn_mode);
- arg1_rtx = get_memory_rtx (arg1);
- arg2_rtx = get_memory_rtx (arg2);
- arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
+ arg1_rtx = get_memory_rtx (arg1, len);
+ arg2_rtx = get_memory_rtx (arg2, len);
+ arg3_rtx = expand_normal (len);
/* Set MEM_SIZE as appropriate. */
if (GET_CODE (arg3_rtx) == CONST_INT)
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)));
+#ifdef HAVE_cmpstrnsi
+ if (HAVE_cmpstrnsi)
+ insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
+ GEN_INT (MIN (arg1_align, arg2_align)));
else
#endif
gcc_unreachable ();
return expand_expr (result, target, mode, EXPAND_NORMAL);
}
-#ifdef HAVE_cmpstrsi
- if (HAVE_cmpstrsi)
- {
- tree arg1 = TREE_VALUE (arglist);
- tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
- tree len, len1, len2;
- rtx arg1_rtx, arg2_rtx, arg3_rtx;
- rtx result, insn;
- tree fndecl;
-
- int arg1_align
- = 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;
-
- len1 = c_strlen (arg1, 1);
- len2 = c_strlen (arg2, 1);
+#if defined HAVE_cmpstrsi || defined HAVE_cmpstrnsi
+ if (cmpstr_optab[SImode] != CODE_FOR_nothing
+ || cmpstrn_optab[SImode] != CODE_FOR_nothing)
+ {
+ rtx arg1_rtx, arg2_rtx;
+ rtx result, insn = NULL_RTX;
+ tree fndecl, fn;
+
+ tree arg1 = TREE_VALUE (arglist);
+ tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+ int arg1_align
+ = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+ int arg2_align
+ = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+
+ /* If we don't have POINTER_TYPE, call the function. */
+ if (arg1_align == 0 || arg2_align == 0)
+ return 0;
- if (len1)
- len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
- if (len2)
- len2 = size_binop (PLUS_EXPR, ssize_int (1), len2);
+ /* Stabilize the arguments in case gen_cmpstr(n)si fail. */
+ arg1 = builtin_save_expr (arg1);
+ arg2 = builtin_save_expr (arg2);
- /* If we don't have a constant length for the first, use the length
- of the second, if we know it. We don't require a constant for
- this case; some cost analysis could be done if both are available
- but neither is constant. For now, assume they're equally cheap,
- unless one has side effects. If both strings have constant lengths,
- use the smaller. */
+ arg1_rtx = get_memory_rtx (arg1, NULL);
+ arg2_rtx = get_memory_rtx (arg2, NULL);
- if (!len1)
- len = len2;
- else if (!len2)
- len = len1;
- else if (TREE_SIDE_EFFECTS (len1))
- len = len2;
- else if (TREE_SIDE_EFFECTS (len2))
- len = len1;
- else if (TREE_CODE (len1) != INTEGER_CST)
- len = len2;
- else if (TREE_CODE (len2) != INTEGER_CST)
- len = len1;
- else if (tree_int_cst_lt (len1, len2))
- len = len1;
- else
- len = len2;
+#ifdef HAVE_cmpstrsi
+ /* Try to call cmpstrsi. */
+ if (HAVE_cmpstrsi)
+ {
+ enum machine_mode insn_mode
+ = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
+
+ /* Make a place to write the result of the instruction. */
+ result = target;
+ if (! (result != 0
+ && REG_P (result) && GET_MODE (result) == insn_mode
+ && REGNO (result) >= FIRST_PSEUDO_REGISTER))
+ result = gen_reg_rtx (insn_mode);
+
+ insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx,
+ GEN_INT (MIN (arg1_align, arg2_align)));
+ }
+#endif
+#if HAVE_cmpstrnsi
+ /* Try to determine at least one length and call cmpstrnsi. */
+ if (!insn && HAVE_cmpstrnsi)
+ {
+ tree len;
+ rtx arg3_rtx;
+
+ enum machine_mode insn_mode
+ = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode;
+ tree len1 = c_strlen (arg1, 1);
+ tree len2 = c_strlen (arg2, 1);
+
+ if (len1)
+ len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
+ if (len2)
+ len2 = size_binop (PLUS_EXPR, ssize_int (1), len2);
+
+ /* If we don't have a constant length for the first, use the length
+ of the second, if we know it. We don't require a constant for
+ this case; some cost analysis could be done if both are available
+ but neither is constant. For now, assume they're equally cheap,
+ unless one has side effects. If both strings have constant lengths,
+ use the smaller. */
+
+ if (!len1)
+ len = len2;
+ else if (!len2)
+ len = len1;
+ else if (TREE_SIDE_EFFECTS (len1))
+ len = len2;
+ else if (TREE_SIDE_EFFECTS (len2))
+ len = len1;
+ else if (TREE_CODE (len1) != INTEGER_CST)
+ len = len2;
+ else if (TREE_CODE (len2) != INTEGER_CST)
+ len = len1;
+ else if (tree_int_cst_lt (len1, len2))
+ len = len1;
+ else
+ len = len2;
- /* If both arguments have side effects, we cannot optimize. */
- if (!len || TREE_SIDE_EFFECTS (len))
- return 0;
+ /* If both arguments have side effects, we cannot optimize. */
+ if (!len || TREE_SIDE_EFFECTS (len))
+ return 0;
- /* If we don't have POINTER_TYPE, call the function. */
- if (arg1_align == 0 || arg2_align == 0)
- return 0;
+ /* Stabilize the arguments in case gen_cmpstrnsi fails. */
+ arg3_rtx = expand_normal (len);
- /* Make a place to write the result of the instruction. */
- result = target;
- if (! (result != 0
- && REG_P (result) && GET_MODE (result) == insn_mode
- && REGNO (result) >= FIRST_PSEUDO_REGISTER))
- result = gen_reg_rtx (insn_mode);
+ /* Make a place to write the result of the instruction. */
+ result = target;
+ if (! (result != 0
+ && REG_P (result) && GET_MODE (result) == insn_mode
+ && REGNO (result) >= FIRST_PSEUDO_REGISTER))
+ result = gen_reg_rtx (insn_mode);
- /* Stabilize the arguments in case gen_cmpstrsi fails. */
- arg1 = builtin_save_expr (arg1);
- arg2 = builtin_save_expr (arg2);
+ insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
+ GEN_INT (MIN (arg1_align, arg2_align)));
+ }
+#endif
- arg1_rtx = get_memory_rtx (arg1);
- arg2_rtx = get_memory_rtx (arg2);
- arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
- insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
- GEN_INT (MIN (arg1_align, arg2_align)));
- if (insn)
- {
- emit_insn (insn);
+ if (insn)
+ {
+ emit_insn (insn);
- /* Return the value in the proper mode for this function. */
- mode = TYPE_MODE (TREE_TYPE (exp));
- if (GET_MODE (result) == mode)
- return result;
- if (target == 0)
- return convert_to_mode (mode, result, 0);
- convert_move (target, result, 0);
- return target;
- }
+ /* Return the value in the proper mode for this function. */
+ mode = TYPE_MODE (TREE_TYPE (exp));
+ if (GET_MODE (result) == mode)
+ return result;
+ if (target == 0)
+ return convert_to_mode (mode, result, 0);
+ convert_move (target, result, 0);
+ return target;
+ }
- /* Expand the library call ourselves using a stabilized argument
- list to avoid re-evaluating the function's arguments twice. */
- 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);
- }
+ /* Expand the library call ourselves using a stabilized argument
+ list to avoid re-evaluating the function's arguments twice. */
+ arglist = build_tree_list (NULL_TREE, arg2);
+ arglist = tree_cons (NULL_TREE, arg1, arglist);
+ fndecl = get_callee_fndecl (exp);
+ 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;
}
}
/* If c_strlen can determine an expression for one of the string
- lengths, and it doesn't have side effects, then emit cmpstrsi
+ lengths, and it doesn't have side effects, then emit cmpstrnsi
using length MIN(strlen(string)+1, arg3). */
-#ifdef HAVE_cmpstrsi
- if (HAVE_cmpstrsi)
+#ifdef HAVE_cmpstrnsi
+ if (HAVE_cmpstrnsi)
{
tree arg1 = TREE_VALUE (arglist);
tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
tree len, len1, len2;
rtx arg1_rtx, arg2_rtx, arg3_rtx;
rtx result, insn;
- tree fndecl;
+ tree fndecl, fn;
int arg1_align
= get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
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;
+ = insn_data[(int) CODE_FOR_cmpstrnsi].operand[0].mode;
len1 = c_strlen (arg1, 1);
len2 = c_strlen (arg2, 1);
return 0;
/* The actual new length parameter is MIN(len,arg3). */
- len = fold (build2 (MIN_EXPR, TREE_TYPE (len), len,
- fold_convert (TREE_TYPE (len), arg3)));
+ len = fold_build2 (MIN_EXPR, TREE_TYPE (len), len,
+ fold_convert (TREE_TYPE (len), arg3));
/* If we don't have POINTER_TYPE, call the function. */
if (arg1_align == 0 || arg2_align == 0)
&& REGNO (result) >= FIRST_PSEUDO_REGISTER))
result = gen_reg_rtx (insn_mode);
- /* Stabilize the arguments in case gen_cmpstrsi fails. */
+ /* Stabilize the arguments in case gen_cmpstrnsi fails. */
arg1 = builtin_save_expr (arg1);
arg2 = builtin_save_expr (arg2);
len = builtin_save_expr (len);
- arg1_rtx = get_memory_rtx (arg1);
- arg2_rtx = get_memory_rtx (arg2);
- arg3_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
- insn = gen_cmpstrsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
- GEN_INT (MIN (arg1_align, arg2_align)));
+ arg1_rtx = get_memory_rtx (arg1, len);
+ arg2_rtx = get_memory_rtx (arg2, len);
+ arg3_rtx = expand_normal (len);
+ insn = gen_cmpstrnsi (result, arg1_rtx, arg2_rtx, arg3_rtx,
+ GEN_INT (MIN (arg1_align, arg2_align)));
if (insn)
{
emit_insn (insn);
arglist = tree_cons (NULL_TREE, arg2, arglist);
arglist = tree_cons (NULL_TREE, arg1, arglist);
fndecl = get_callee_fndecl (exp);
- exp = build_function_call_expr (fndecl, arglist);
- return expand_call (exp, target, target == const0_rtx);
+ fn = build_function_call_expr (fndecl, arglist);
+ if (TREE_CODE (fn) == CALL_EXPR)
+ CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+ return expand_call (fn, target, target == const0_rtx);
}
#endif
return 0;
otherwise try to get the result in TARGET, if convenient. */
static rtx
-expand_builtin_strcat (tree arglist, tree type, rtx target, enum machine_mode mode)
+expand_builtin_strcat (tree fndecl, tree arglist, rtx target, enum machine_mode mode)
{
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE))
return 0;
else
{
tree dst = TREE_VALUE (arglist),
- src = TREE_VALUE (TREE_CHAIN (arglist));
+ src = TREE_VALUE (TREE_CHAIN (arglist));
const char *p = c_getstr (src);
- 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)
+ /* If the string length is zero, return the dst parameter. */
+ if (p && *p == '\0')
+ return expand_expr (dst, target, mode, EXPAND_NORMAL);
+
+ if (!optimize_size)
+ {
+ /* See if we can store by pieces into (dst + strlen(dst)). */
+ tree newsrc, newdst,
+ strlen_fn = implicit_built_in_decls[BUILT_IN_STRLEN];
+ rtx insns;
+
+ /* Stabilize the argument list. */
+ newsrc = builtin_save_expr (src);
+ if (newsrc != src)
+ arglist = build_tree_list (NULL_TREE, newsrc);
+ else
+ arglist = TREE_CHAIN (arglist); /* Reusing arglist if safe. */
+
+ dst = builtin_save_expr (dst);
+
+ start_sequence ();
+
+ /* Create strlen (dst). */
+ newdst =
+ build_function_call_expr (strlen_fn,
+ build_tree_list (NULL_TREE, dst));
+ /* Create (dst + (cast) strlen (dst)). */
+ newdst = fold_convert (TREE_TYPE (dst), newdst);
+ newdst = fold_build2 (PLUS_EXPR, TREE_TYPE (dst), dst, newdst);
+
+ newdst = builtin_save_expr (newdst);
+ arglist = tree_cons (NULL_TREE, newdst, arglist);
+
+ if (!expand_builtin_strcpy (fndecl, arglist, target, mode))
{
- /* 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 = builtin_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 (build2 (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, type, /*target=*/0, mode, /*endp=*/0))
- return expand_expr (dst, target, mode, EXPAND_NORMAL);
- else
- return 0;
+ end_sequence (); /* Stop sequence. */
+ return 0;
}
+
+ /* Output the entire sequence. */
+ insns = get_insns ();
+ end_sequence ();
+ emit_insn (insns);
+
+ return expand_expr (dst, target, mode, EXPAND_NORMAL);
}
return 0;
return valist;
pt = build_pointer_type (va_list_type_node);
- valist = fold (build1 (ADDR_EXPR, pt, valist));
+ valist = fold_build1 (ADDR_EXPR, pt, valist);
TREE_SIDE_EFFECTS (valist) = 1;
}
/* va_list pointer is aligned to PARM_BOUNDARY. If argument actually
requires greater alignment, we must perform dynamic alignment. */
- if (boundary > align)
+ if (boundary > align
+ && !integer_zerop (TYPE_SIZE (type)))
{
t = fold_convert (TREE_TYPE (valist), size_int (boundary - 1));
t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
if (PAD_VARARGS_DOWN && !integer_zerop (rounded_size))
{
/* Small args are padded downward. */
- t = fold (build2 (GT_EXPR, sizetype, rounded_size, size_int (align)));
- t = fold (build3 (COND_EXPR, sizetype, t, size_zero_node,
- size_binop (MINUS_EXPR, rounded_size, type_size)));
+ t = fold_build2 (GT_EXPR, sizetype, rounded_size, size_int (align));
+ t = fold_build3 (COND_EXPR, sizetype, t, size_zero_node,
+ size_binop (MINUS_EXPR, rounded_size, type_size));
t = fold_convert (TREE_TYPE (addr), t);
- addr = fold (build2 (PLUS_EXPR, TREE_TYPE (addr), addr, t));
+ addr = fold_build2 (PLUS_EXPR, TREE_TYPE (addr), addr, t);
}
/* Compute new value for AP. */
/* 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);
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 0;
/* Compute the argument. */
- op0 = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0);
+ op0 = expand_normal (TREE_VALUE (arglist));
/* Allocate the desired space. */
result = allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT);
return ret;
}
-static void
+void
expand_builtin_trap (void)
{
#ifdef HAVE_trap
return 0;
arg = TREE_VALUE (arglist);
- op0 = expand_expr (arg, subtarget, VOIDmode, 0);
+ op0 = expand_expr (arg, subtarget, VOIDmode, EXPAND_NORMAL);
arg = TREE_VALUE (TREE_CHAIN (arglist));
- op1 = expand_expr (arg, NULL, VOIDmode, 0);
+ op1 = expand_normal (arg);
return expand_copysign (op0, op1, target);
}
/* Create a new constant string literal and return a char* pointer to it.
The STRING_CST value is the LEN characters at STR. */
-static tree
+tree
build_string_literal (int len, const char *str)
{
tree t, elem, index, type;
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 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];
+ tree arglist = TREE_OPERAND (exp, 1);
+ /* If we're using an unlocked function, assume the other unlocked
+ functions exist explicitly. */
+ tree const fn_putchar = unlocked ? built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_PUTCHAR];
+ tree const fn_puts = unlocked ? built_in_decls[BUILT_IN_PUTS_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_PUTS];
const char *fmt_str;
tree fn, fmt, arg;
if (fmt_str == NULL)
return 0;
+ if (!init_target_chars())
+ return 0;
+
/* If the format specifier was "%s\n", call __builtin_puts(arg). */
- if (strcmp (fmt_str, "%s\n") == 0)
+ if (strcmp (fmt_str, target_percent_s_newline) == 0)
{
if (! arglist
|| ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
fn = fn_puts;
}
/* If the format specifier was "%c", call __builtin_putchar(arg). */
- else if (strcmp (fmt_str, "%c") == 0)
+ else if (strcmp (fmt_str, target_percent_c) == 0)
{
if (! arglist
|| TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
else
{
/* We can't handle anything else with % args or %% ... yet. */
- if (strchr (fmt_str, '%'))
+ if (strchr (fmt_str, target_percent))
return 0;
if (arglist)
{
/* If the format specifier was "string\n", call puts("string"). */
size_t len = strlen (fmt_str);
- if (fmt_str[len - 1] == '\n')
+ if ((unsigned char)fmt_str[len - 1] == target_newline)
{
/* Create a NUL-terminated string that's one char shorter
than the original, stripping off the trailing '\n'. */
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 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];
+ tree arglist = TREE_OPERAND (exp, 1);
+ /* If we're using an unlocked function, assume the other unlocked
+ functions exist explicitly. */
+ tree const fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_FPUTC];
+ tree const fn_fputs = unlocked ? built_in_decls[BUILT_IN_FPUTS_UNLOCKED]
+ : implicit_built_in_decls[BUILT_IN_FPUTS];
const char *fmt_str;
tree fn, fmt, fp, arg;
if (fmt_str == NULL)
return 0;
+ if (!init_target_chars())
+ return 0;
+
/* If the format specifier was "%s", call __builtin_fputs(arg,fp). */
- if (strcmp (fmt_str, "%s") == 0)
+ if (strcmp (fmt_str, target_percent_s) == 0)
{
if (! arglist
|| ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
fn = fn_fputs;
}
/* If the format specifier was "%c", call __builtin_fputc(arg,fp). */
- else if (strcmp (fmt_str, "%c") == 0)
+ else if (strcmp (fmt_str, target_percent_c) == 0)
{
if (! arglist
|| TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
else
{
/* We can't handle anything else with % args or %% ... yet. */
- if (strchr (fmt_str, '%'))
+ if (strchr (fmt_str, target_percent))
return 0;
if (arglist)
if (!fn)
return 0;
- return expand_expr (build_function_call_expr (fn, arglist),
- target, mode, EXPAND_NORMAL);
+ fn = build_function_call_expr (fn, arglist);
+ if (TREE_CODE (fn) == CALL_EXPR)
+ CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+ return expand_expr (fn, target, mode, EXPAND_NORMAL);
}
/* Expand a call to sprintf with argument list ARGLIST. Return 0 if
if (fmt_str == NULL)
return 0;
+ if (!init_target_chars())
+ return 0;
+
/* If the format doesn't contain % args or %%, use strcpy. */
- if (strchr (fmt_str, '%') == 0)
+ if (strchr (fmt_str, target_percent) == 0)
{
tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
tree exp;
return expand_expr (exp, target, mode, EXPAND_NORMAL);
}
/* If the format is "%s", use strcpy if the result isn't used. */
- else if (strcmp (fmt_str, "%s") == 0)
+ else if (strcmp (fmt_str, target_percent_s) == 0)
{
tree fn, arg, len;
fn = implicit_built_in_decls[BUILT_IN_STRCPY];
arglist = TREE_CHAIN (arglist);
t_chain = TREE_VALUE (arglist);
- r_tramp = expand_expr (t_tramp, NULL_RTX, VOIDmode, 0);
- r_func = expand_expr (t_func, NULL_RTX, VOIDmode, 0);
- r_chain = expand_expr (t_chain, NULL_RTX, VOIDmode, 0);
+ r_tramp = expand_normal (t_tramp);
+ r_func = expand_normal (t_func);
+ r_chain = expand_normal (t_chain);
/* Generate insns to initialize the trampoline. */
r_tramp = round_trampoline_addr (r_tramp);
if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
return NULL_RTX;
- tramp = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0);
+ tramp = expand_normal (TREE_VALUE (arglist));
tramp = round_trampoline_addr (tramp);
#ifdef TRAMPOLINE_ADJUST_ADDRESS
TRAMPOLINE_ADJUST_ADDRESS (tramp);
/* For floating point formats without a sign bit, implement signbit
as "ARG < 0.0". */
- bitpos = fmt->signbit;
+ 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 0;
- arg = fold (build2 (LT_EXPR, TREE_TYPE (exp), arg,
- build_real (TREE_TYPE (arg), dconst0)));
+ arg = fold_build2 (LT_EXPR, TREE_TYPE (exp), arg,
+ build_real (TREE_TYPE (arg), dconst0));
return expand_expr (arg, target, VOIDmode, EXPAND_NORMAL);
}
- temp = expand_expr (arg, NULL_RTX, VOIDmode, 0);
+ temp = expand_normal (arg);
if (GET_MODE_SIZE (fmode) <= UNITS_PER_WORD)
{
imode = int_mode_for_mode (fmode);
return expand_call (call, target, ignore);
}
+
+\f
+/* Reconstitute a mode for a __sync intrinsic operation. Since the type of
+ the pointer in these functions is void*, the tree optimizers may remove
+ casts. The mode computed in expand_builtin isn't reliable either, due
+ to __sync_bool_compare_and_swap.
+
+ FCODE_DIFF should be fcode - base, where base is the FOO_1 code for the
+ group of builtins. This gives us log2 of the mode size. */
+
+static inline enum machine_mode
+get_builtin_sync_mode (int fcode_diff)
+{
+ /* The size is not negotiable, so ask not to get BLKmode in return
+ if the target indicates that a smaller size would be better. */
+ return mode_for_size (BITS_PER_UNIT << fcode_diff, MODE_INT, 0);
+}
+
+/* Expand the memory expression LOC and return the appropriate memory operand
+ for the builtin_sync operations. */
+
+static rtx
+get_builtin_sync_mem (tree loc, enum machine_mode mode)
+{
+ rtx addr, mem;
+
+ addr = expand_expr (loc, NULL, Pmode, EXPAND_SUM);
+
+ /* 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));
+
+ set_mem_align (mem, get_pointer_alignment (loc, BIGGEST_ALIGNMENT));
+ set_mem_alias_set (mem, ALIAS_SET_MEMORY_BARRIER);
+ MEM_VOLATILE_P (mem) = 1;
+
+ return mem;
+}
+
+/* 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 (enum machine_mode mode, tree arglist,
+ enum rtx_code code, bool after,
+ rtx target, bool ignore)
+{
+ rtx val, mem;
+
+ /* Expand the operands. */
+ mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode);
+
+ arglist = TREE_CHAIN (arglist);
+ val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+ 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 (enum machine_mode mode, tree arglist,
+ bool is_bool, rtx target)
+{
+ rtx old_val, new_val, mem;
+
+ /* Expand the operands. */
+ mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode);
+
+ 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);
+
+ 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 (enum machine_mode mode, tree arglist,
+ rtx target)
+{
+ rtx val, mem;
+
+ /* Expand the operands. */
+ mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode);
+
+ arglist = TREE_CHAIN (arglist);
+ val = expand_expr (TREE_VALUE (arglist), NULL, mode, EXPAND_NORMAL);
+
+ return expand_sync_lock_test_and_set (mem, val, target);
+}
+
+/* Expand the __sync_synchronize intrinsic. */
+
+static void
+expand_builtin_synchronize (void)
+{
+ tree x;
+
+#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 with a memory clobber. */
+ x = build4 (ASM_EXPR, void_type_node, build_string (0, ""), NULL, NULL,
+ tree_cons (NULL, build_string (6, "memory"), NULL));
+ ASM_VOLATILE_P (x) = 1;
+ expand_asm_expr (x);
+}
+
+/* Expand the __sync_lock_release intrinsic. ARGLIST is the operands list
+ to the function. */
+
+static void
+expand_builtin_lock_release (enum machine_mode mode, tree arglist)
+{
+ enum insn_code icode;
+ rtx mem, insn;
+ rtx val = const0_rtx;
+
+ /* Expand the operands. */
+ mem = get_builtin_sync_mem (TREE_VALUE (arglist), mode);
+
+ /* 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
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
return targetm.expand_builtin (exp, target, subtarget, mode, ignore);
+ else
+ {
+ /* Try expanding the builtin via the generic target hook. */
+ rtx tmp = targetm.expand_library_builtin (exp, target, subtarget,
+ mode, ignore);
+ if (tmp != NULL_RTX)
+ return tmp;
+ }
/* 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);
switch (fcode)
{
- case BUILT_IN_FABS:
- case BUILT_IN_FABSF:
- case BUILT_IN_FABSL:
+ CASE_FLT_FN (BUILT_IN_FABS):
target = expand_builtin_fabs (arglist, target, subtarget);
if (target)
return target;
break;
- case BUILT_IN_COPYSIGN:
- case BUILT_IN_COPYSIGNF:
- case BUILT_IN_COPYSIGNL:
+ CASE_FLT_FN (BUILT_IN_COPYSIGN):
target = expand_builtin_copysign (arglist, target, subtarget);
if (target)
return target;
/* Just do a normal library call if we were unable to fold
the values. */
- case BUILT_IN_CABS:
- case BUILT_IN_CABSF:
- case BUILT_IN_CABSL:
+ CASE_FLT_FN (BUILT_IN_CABS):
break;
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
- case BUILT_IN_EXP10:
- case BUILT_IN_EXP10F:
- case BUILT_IN_EXP10L:
- case BUILT_IN_POW10:
- case BUILT_IN_POW10F:
- case BUILT_IN_POW10L:
- case BUILT_IN_EXP2:
- case BUILT_IN_EXP2F:
- case BUILT_IN_EXP2L:
- case BUILT_IN_EXPM1:
- case BUILT_IN_EXPM1F:
- case BUILT_IN_EXPM1L:
- case BUILT_IN_LOGB:
- case BUILT_IN_LOGBF:
- case BUILT_IN_LOGBL:
- case BUILT_IN_ILOGB:
- case BUILT_IN_ILOGBF:
- case BUILT_IN_ILOGBL:
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
- case BUILT_IN_LOG10:
- case BUILT_IN_LOG10F:
- case BUILT_IN_LOG10L:
- case BUILT_IN_LOG2:
- case BUILT_IN_LOG2F:
- case BUILT_IN_LOG2L:
- case BUILT_IN_LOG1P:
- case BUILT_IN_LOG1PF:
- case BUILT_IN_LOG1PL:
- case BUILT_IN_TAN:
- case BUILT_IN_TANF:
- case BUILT_IN_TANL:
- case BUILT_IN_ASIN:
- case BUILT_IN_ASINF:
- case BUILT_IN_ASINL:
- case BUILT_IN_ACOS:
- case BUILT_IN_ACOSF:
- case BUILT_IN_ACOSL:
- case BUILT_IN_ATAN:
- case BUILT_IN_ATANF:
- case BUILT_IN_ATANL:
+ CASE_FLT_FN (BUILT_IN_EXP):
+ CASE_FLT_FN (BUILT_IN_EXP10):
+ CASE_FLT_FN (BUILT_IN_POW10):
+ CASE_FLT_FN (BUILT_IN_EXP2):
+ CASE_FLT_FN (BUILT_IN_EXPM1):
+ CASE_FLT_FN (BUILT_IN_LOGB):
+ CASE_FLT_FN (BUILT_IN_ILOGB):
+ CASE_FLT_FN (BUILT_IN_LOG):
+ CASE_FLT_FN (BUILT_IN_LOG10):
+ CASE_FLT_FN (BUILT_IN_LOG2):
+ CASE_FLT_FN (BUILT_IN_LOG1P):
+ CASE_FLT_FN (BUILT_IN_TAN):
+ CASE_FLT_FN (BUILT_IN_ASIN):
+ CASE_FLT_FN (BUILT_IN_ACOS):
+ CASE_FLT_FN (BUILT_IN_ATAN):
/* Treat these like sqrt only if unsafe math optimizations are allowed,
because of possible accuracy problems. */
if (! flag_unsafe_math_optimizations)
break;
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
- 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:
- case BUILT_IN_RINT:
- case BUILT_IN_RINTF:
- case BUILT_IN_RINTL:
+ CASE_FLT_FN (BUILT_IN_SQRT):
+ CASE_FLT_FN (BUILT_IN_FLOOR):
+ CASE_FLT_FN (BUILT_IN_CEIL):
+ CASE_FLT_FN (BUILT_IN_TRUNC):
+ CASE_FLT_FN (BUILT_IN_ROUND):
+ CASE_FLT_FN (BUILT_IN_NEARBYINT):
+ CASE_FLT_FN (BUILT_IN_RINT):
+ CASE_FLT_FN (BUILT_IN_LRINT):
+ CASE_FLT_FN (BUILT_IN_LLRINT):
target = expand_builtin_mathfn (exp, target, subtarget);
if (target)
return target;
break;
- case BUILT_IN_POW:
- case BUILT_IN_POWF:
- case BUILT_IN_POWL:
+ CASE_FLT_FN (BUILT_IN_LCEIL):
+ CASE_FLT_FN (BUILT_IN_LLCEIL):
+ CASE_FLT_FN (BUILT_IN_LFLOOR):
+ CASE_FLT_FN (BUILT_IN_LLFLOOR):
+ target = expand_builtin_int_roundingfn (exp, target, subtarget);
+ if (target)
+ return target;
+ break;
+
+ CASE_FLT_FN (BUILT_IN_POW):
target = expand_builtin_pow (exp, target, subtarget);
if (target)
return target;
break;
- case BUILT_IN_POWI:
- case BUILT_IN_POWIF:
- case BUILT_IN_POWIL:
+ CASE_FLT_FN (BUILT_IN_POWI):
target = expand_builtin_powi (exp, target, subtarget);
if (target)
return target;
break;
- case BUILT_IN_ATAN2:
- case BUILT_IN_ATAN2F:
- case BUILT_IN_ATAN2L:
- case BUILT_IN_LDEXP:
- case BUILT_IN_LDEXPF:
- case BUILT_IN_LDEXPL:
- case BUILT_IN_FMOD:
- case BUILT_IN_FMODF:
- case BUILT_IN_FMODL:
- case BUILT_IN_DREM:
- case BUILT_IN_DREMF:
- case BUILT_IN_DREML:
+ CASE_FLT_FN (BUILT_IN_ATAN2):
+ CASE_FLT_FN (BUILT_IN_LDEXP):
+ CASE_FLT_FN (BUILT_IN_FMOD):
+ CASE_FLT_FN (BUILT_IN_DREM):
if (! flag_unsafe_math_optimizations)
break;
target = expand_builtin_mathfn_2 (exp, target, subtarget);
return target;
break;
- 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_FLT_FN (BUILT_IN_SIN):
+ CASE_FLT_FN (BUILT_IN_COS):
if (! flag_unsafe_math_optimizations)
break;
target = expand_builtin_mathfn_3 (exp, target, subtarget);
return target;
break;
+ CASE_FLT_FN (BUILT_IN_SINCOS):
+ if (! flag_unsafe_math_optimizations)
+ break;
+ target = expand_builtin_sincos (exp);
+ if (target)
+ return target;
+ break;
+
case BUILT_IN_APPLY_ARGS:
return expand_builtin_apply_args ();
rtx ops[3];
for (t = arglist, i = 0; t; t = TREE_CHAIN (t), i++)
- ops[i] = expand_expr (TREE_VALUE (t), NULL_RTX, VOIDmode, 0);
+ ops[i] = expand_normal (TREE_VALUE (t));
return expand_builtin_apply (ops[0], ops[1], ops[2]);
}
memory returned by __builtin_apply. */
case BUILT_IN_RETURN:
if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
- expand_builtin_return (expand_expr (TREE_VALUE (arglist),
- NULL_RTX, VOIDmode, 0));
+ expand_builtin_return (expand_normal (TREE_VALUE (arglist)));
return const0_rtx;
case BUILT_IN_SAVEREGS:
expand_stack_restore (TREE_VALUE (arglist));
return const0_rtx;
- case BUILT_IN_FFS:
- case BUILT_IN_FFSL:
- case BUILT_IN_FFSLL:
+ CASE_INT_FN (BUILT_IN_FFS):
case BUILT_IN_FFSIMAX:
target = expand_builtin_unop (target_mode, arglist, target,
subtarget, ffs_optab);
return target;
break;
- case BUILT_IN_CLZ:
- case BUILT_IN_CLZL:
- case BUILT_IN_CLZLL:
+ CASE_INT_FN (BUILT_IN_CLZ):
case BUILT_IN_CLZIMAX:
target = expand_builtin_unop (target_mode, arglist, target,
subtarget, clz_optab);
return target;
break;
- case BUILT_IN_CTZ:
- case BUILT_IN_CTZL:
- case BUILT_IN_CTZLL:
+ CASE_INT_FN (BUILT_IN_CTZ):
case BUILT_IN_CTZIMAX:
target = expand_builtin_unop (target_mode, arglist, target,
subtarget, ctz_optab);
return target;
break;
- case BUILT_IN_POPCOUNT:
- case BUILT_IN_POPCOUNTL:
- case BUILT_IN_POPCOUNTLL:
+ CASE_INT_FN (BUILT_IN_POPCOUNT):
case BUILT_IN_POPCOUNTIMAX:
target = expand_builtin_unop (target_mode, arglist, target,
subtarget, popcount_optab);
return target;
break;
- case BUILT_IN_PARITY:
- case BUILT_IN_PARITYL:
- case BUILT_IN_PARITYLL:
+ CASE_INT_FN (BUILT_IN_PARITY):
case BUILT_IN_PARITYIMAX:
target = expand_builtin_unop (target_mode, arglist, target,
subtarget, parity_optab);
break;
case BUILT_IN_STRCPY:
- target = expand_builtin_strcpy (exp, target, mode);
+ target = expand_builtin_strcpy (fndecl, arglist, target, mode);
if (target)
return target;
break;
break;
case BUILT_IN_STRCAT:
- target = expand_builtin_strcat (arglist, TREE_TYPE (exp), target, mode);
+ target = expand_builtin_strcat (fndecl, arglist, 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;
else
{
rtx buf_addr = expand_expr (TREE_VALUE (arglist), subtarget,
- VOIDmode, 0);
- rtx value = expand_expr (TREE_VALUE (TREE_CHAIN (arglist)),
- NULL_RTX, VOIDmode, 0);
+ VOIDmode, EXPAND_NORMAL);
+ rtx value = expand_normal (TREE_VALUE (TREE_CHAIN (arglist)));
if (value != const1_rtx)
{
if (validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
{
rtx buf_addr
- = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0);
+ = expand_normal (TREE_VALUE (arglist));
expand_builtin_update_setjmp_buf (buf_addr);
return const0_rtx;
return const0_rtx;
case BUILT_IN_PRINTF:
- target = expand_builtin_printf (arglist, target, mode, false);
+ target = expand_builtin_printf (exp, target, mode, false);
if (target)
return target;
break;
case BUILT_IN_PRINTF_UNLOCKED:
- target = expand_builtin_printf (arglist, target, mode, true);
+ target = expand_builtin_printf (exp, target, mode, true);
if (target)
return target;
break;
break;
case BUILT_IN_FPRINTF:
- target = expand_builtin_fprintf (arglist, target, mode, false);
+ target = expand_builtin_fprintf (exp, target, mode, false);
if (target)
return target;
break;
case BUILT_IN_FPRINTF_UNLOCKED:
- target = expand_builtin_fprintf (arglist, target, mode, true);
+ target = expand_builtin_fprintf (exp, target, mode, true);
if (target)
return target;
break;
return target;
break;
- case BUILT_IN_SIGNBIT:
- case BUILT_IN_SIGNBITF:
- case BUILT_IN_SIGNBITL:
+ CASE_FLT_FN (BUILT_IN_SIGNBIT):
target = expand_builtin_signbit (exp, target);
if (target)
return target;
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:
+ case BUILT_IN_FETCH_AND_ADD_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_ADD_1);
+ target = expand_builtin_sync_operation (mode, 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:
+ case BUILT_IN_FETCH_AND_SUB_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_SUB_1);
+ target = expand_builtin_sync_operation (mode, 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:
+ case BUILT_IN_FETCH_AND_OR_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_OR_1);
+ target = expand_builtin_sync_operation (mode, 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:
+ case BUILT_IN_FETCH_AND_AND_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_AND_1);
+ target = expand_builtin_sync_operation (mode, 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:
+ case BUILT_IN_FETCH_AND_XOR_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_XOR_1);
+ target = expand_builtin_sync_operation (mode, 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:
+ case BUILT_IN_FETCH_AND_NAND_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_FETCH_AND_NAND_1);
+ target = expand_builtin_sync_operation (mode, 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:
+ case BUILT_IN_ADD_AND_FETCH_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_ADD_AND_FETCH_1);
+ target = expand_builtin_sync_operation (mode, 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:
+ case BUILT_IN_SUB_AND_FETCH_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_SUB_AND_FETCH_1);
+ target = expand_builtin_sync_operation (mode, 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:
+ case BUILT_IN_OR_AND_FETCH_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_OR_AND_FETCH_1);
+ target = expand_builtin_sync_operation (mode, 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:
+ case BUILT_IN_AND_AND_FETCH_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_AND_AND_FETCH_1);
+ target = expand_builtin_sync_operation (mode, 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:
+ case BUILT_IN_XOR_AND_FETCH_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_XOR_AND_FETCH_1);
+ target = expand_builtin_sync_operation (mode, 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:
+ case BUILT_IN_NAND_AND_FETCH_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_NAND_AND_FETCH_1);
+ target = expand_builtin_sync_operation (mode, 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:
+ case BUILT_IN_BOOL_COMPARE_AND_SWAP_16:
+ if (mode == VOIDmode)
+ mode = TYPE_MODE (boolean_type_node);
+ if (!target || !register_operand (target, mode))
+ target = gen_reg_rtx (mode);
+
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_BOOL_COMPARE_AND_SWAP_1);
+ target = expand_builtin_compare_and_swap (mode, 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:
+ case BUILT_IN_VAL_COMPARE_AND_SWAP_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_VAL_COMPARE_AND_SWAP_1);
+ target = expand_builtin_compare_and_swap (mode, 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:
+ case BUILT_IN_LOCK_TEST_AND_SET_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_LOCK_TEST_AND_SET_1);
+ target = expand_builtin_lock_test_and_set (mode, 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:
+ case BUILT_IN_LOCK_RELEASE_16:
+ mode = get_builtin_sync_mode (fcode - BUILT_IN_LOCK_RELEASE_1);
+ expand_builtin_lock_release (mode, arglist);
+ return const0_rtx;
+
+ case BUILT_IN_SYNCHRONIZE:
+ expand_builtin_synchronize ();
+ return const0_rtx;
+
+ case BUILT_IN_OBJECT_SIZE:
+ return expand_builtin_object_size (exp);
+
+ case BUILT_IN_MEMCPY_CHK:
+ case BUILT_IN_MEMPCPY_CHK:
+ case BUILT_IN_MEMMOVE_CHK:
+ case BUILT_IN_MEMSET_CHK:
+ target = expand_builtin_memory_chk (exp, target, mode, fcode);
+ if (target)
+ return target;
+ break;
+
+ case BUILT_IN_STRCPY_CHK:
+ case BUILT_IN_STPCPY_CHK:
+ case BUILT_IN_STRNCPY_CHK:
+ case BUILT_IN_STRCAT_CHK:
+ case BUILT_IN_SNPRINTF_CHK:
+ case BUILT_IN_VSNPRINTF_CHK:
+ maybe_emit_chk_warning (exp, fcode);
+ break;
+
+ case BUILT_IN_SPRINTF_CHK:
+ case BUILT_IN_VSPRINTF_CHK:
+ maybe_emit_sprintf_chk_warning (exp, fcode);
+ break;
+
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
real_trunc (&cint, TYPE_MODE (TREE_TYPE (t)), &c);
return real_identical (&c, &cint);
}
+ break;
case NOP_EXPR:
{
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_RINT:
- case BUILT_IN_RINTF:
- case BUILT_IN_RINTL:
- case BUILT_IN_ROUND:
- case BUILT_IN_ROUNDF:
- case BUILT_IN_ROUNDL:
- case BUILT_IN_TRUNC:
- case BUILT_IN_TRUNCF:
- case BUILT_IN_TRUNCL:
+ CASE_FLT_FN (BUILT_IN_CEIL):
+ CASE_FLT_FN (BUILT_IN_FLOOR):
+ CASE_FLT_FN (BUILT_IN_NEARBYINT):
+ CASE_FLT_FN (BUILT_IN_RINT):
+ CASE_FLT_FN (BUILT_IN_ROUND):
+ CASE_FLT_FN (BUILT_IN_TRUNC):
return true;
default:
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 either part is zero, cabs is fabs of the other. */
if (TREE_CODE (arg) == COMPLEX_EXPR
&& real_zerop (TREE_OPERAND (arg, 0)))
- return fold (build1 (ABS_EXPR, type, TREE_OPERAND (arg, 1)));
+ return fold_build1 (ABS_EXPR, type, TREE_OPERAND (arg, 1));
if (TREE_CODE (arg) == COMPLEX_EXPR
&& real_zerop (TREE_OPERAND (arg, 1)))
- return fold (build1 (ABS_EXPR, type, TREE_OPERAND (arg, 0)));
+ return fold_build1 (ABS_EXPR, type, TREE_OPERAND (arg, 0));
/* Don't do this when optimizing for size. */
if (flag_unsafe_math_optimizations
arg = builtin_save_expr (arg);
- rpart = fold (build1 (REALPART_EXPR, type, arg));
- ipart = fold (build1 (IMAGPART_EXPR, type, arg));
+ rpart = fold_build1 (REALPART_EXPR, type, arg);
+ ipart = fold_build1 (IMAGPART_EXPR, type, arg);
rpart = builtin_save_expr (rpart);
ipart = builtin_save_expr (ipart);
- result = fold (build2 (PLUS_EXPR, type,
- fold (build2 (MULT_EXPR, type,
- rpart, rpart)),
- fold (build2 (MULT_EXPR, type,
- ipart, ipart))));
+ result = fold_build2 (PLUS_EXPR, type,
+ fold_build2 (MULT_EXPR, type,
+ rpart, rpart),
+ fold_build2 (MULT_EXPR, type,
+ ipart, ipart));
arglist = build_tree_list (NULL_TREE, result);
return build_function_call_expr (sqrtfn, arglist);
if (flag_unsafe_math_optimizations && BUILTIN_EXPONENT_P (fcode))
{
tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
- arg = fold (build2 (MULT_EXPR, type,
- TREE_VALUE (TREE_OPERAND (arg, 1)),
- build_real (type, dconsthalf)));
+ arg = fold_build2 (MULT_EXPR, type,
+ TREE_VALUE (TREE_OPERAND (arg, 1)),
+ build_real (type, dconsthalf));
arglist = build_tree_list (NULL_TREE, arg);
return build_function_call_expr (expfn, arglist);
}
tree narg1;
if (!tree_expr_nonnegative_p (arg0))
arg0 = build1 (ABS_EXPR, type, arg0);
- narg1 = fold (build2 (MULT_EXPR, type, arg1,
- build_real (type, dconsthalf)));
+ narg1 = fold_build2 (MULT_EXPR, type, arg1,
+ build_real (type, dconsthalf));
arglist = tree_cons (NULL_TREE, arg0,
build_tree_list (NULL_TREE, narg1));
return build_function_call_expr (powfn, arglist);
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)));
+ 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);
}
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)));
+ 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);
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_FLT_FN (BUILT_IN_LFLOOR):
+ CASE_FLT_FN (BUILT_IN_LLFLOOR):
+ real_floor (&r, TYPE_MODE (ftype), &x);
+ break;
+
+ CASE_FLT_FN (BUILT_IN_LCEIL):
+ CASE_FLT_FN (BUILT_IN_LLCEIL):
+ real_ceil (&r, TYPE_MODE (ftype), &x);
+ break;
+
+ CASE_FLT_FN (BUILT_IN_LROUND):
+ CASE_FLT_FN (BUILT_IN_LLROUND):
+ 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))
switch (DECL_FUNCTION_CODE (fndecl))
{
- case BUILT_IN_FFS:
- case BUILT_IN_FFSL:
- case BUILT_IN_FFSLL:
+ CASE_INT_FN (BUILT_IN_FFS):
if (lo != 0)
result = exact_log2 (lo & -lo) + 1;
else if (hi != 0)
result = 0;
break;
- case BUILT_IN_CLZ:
- case BUILT_IN_CLZL:
- case BUILT_IN_CLZLL:
+ CASE_INT_FN (BUILT_IN_CLZ):
if (hi != 0)
result = width - floor_log2 (hi) - 1 - HOST_BITS_PER_WIDE_INT;
else if (lo != 0)
result = width;
break;
- case BUILT_IN_CTZ:
- case BUILT_IN_CTZL:
- case BUILT_IN_CTZLL:
+ CASE_INT_FN (BUILT_IN_CTZ):
if (lo != 0)
result = exact_log2 (lo & -lo);
else if (hi != 0)
result = width;
break;
- case BUILT_IN_POPCOUNT:
- case BUILT_IN_POPCOUNTL:
- case BUILT_IN_POPCOUNTLL:
+ CASE_INT_FN (BUILT_IN_POPCOUNT):
result = 0;
while (lo)
result++, lo &= lo - 1;
result++, hi &= hi - 1;
break;
- case BUILT_IN_PARITY:
- case BUILT_IN_PARITYL:
- case BUILT_IN_PARITYLL:
+ CASE_INT_FN (BUILT_IN_PARITY):
result = 0;
while (lo)
result++, lo &= lo - 1;
gcc_unreachable ();
}
- return build_int_cst (TREE_TYPE (exp), result);
+ return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), result);
}
return NULL_TREE;
switch (fcode)
{
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
+ CASE_FLT_FN (BUILT_IN_EXP):
/* Prepare to do logN(exp(exponent) -> exponent*logN(e). */
x = build_real (type,
real_value_truncate (TYPE_MODE (type), dconste));
exponent = TREE_VALUE (TREE_OPERAND (arg, 1));
break;
- case BUILT_IN_EXP2:
- case BUILT_IN_EXP2F:
- case BUILT_IN_EXP2L:
+ CASE_FLT_FN (BUILT_IN_EXP2):
/* Prepare to do logN(exp2(exponent) -> exponent*logN(2). */
x = build_real (type, dconst2);
exponent = TREE_VALUE (TREE_OPERAND (arg, 1));
break;
- case BUILT_IN_EXP10:
- case BUILT_IN_EXP10F:
- case BUILT_IN_EXP10L:
- case BUILT_IN_POW10:
- case BUILT_IN_POW10F:
- case BUILT_IN_POW10L:
+ CASE_FLT_FN (BUILT_IN_EXP10):
+ CASE_FLT_FN (BUILT_IN_POW10):
/* Prepare to do logN(exp10(exponent) -> exponent*logN(10). */
x = build_real (type, dconst10);
exponent = TREE_VALUE (TREE_OPERAND (arg, 1));
break;
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
+ CASE_FLT_FN (BUILT_IN_SQRT):
/* Prepare to do logN(sqrt(x) -> 0.5*logN(x). */
x = TREE_VALUE (TREE_OPERAND (arg, 1));
exponent = build_real (type, dconsthalf);
break;
- case BUILT_IN_CBRT:
- case BUILT_IN_CBRTF:
- case BUILT_IN_CBRTL:
+ CASE_FLT_FN (BUILT_IN_CBRT):
/* Prepare to do logN(cbrt(x) -> (1/3)*logN(x). */
x = TREE_VALUE (TREE_OPERAND (arg, 1));
exponent = build_real (type, real_value_truncate (TYPE_MODE (type),
dconstthird));
break;
- case BUILT_IN_POW:
- case BUILT_IN_POWF:
- case BUILT_IN_POWL:
+ CASE_FLT_FN (BUILT_IN_POW):
/* Prepare to do logN(pow(x,exponent) -> exponent*logN(x). */
x = TREE_VALUE (TREE_OPERAND (arg, 1));
exponent = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg, 1)));
tree logfn;
arglist = build_tree_list (NULL_TREE, x);
logfn = build_function_call_expr (fndecl, arglist);
- return fold (build2 (MULT_EXPR, type, exponent, logfn));
+ return fold_build2 (MULT_EXPR, type, exponent, logfn);
}
}
}
/* Optimize pow(x,-1.0) = 1.0/x. */
if (REAL_VALUES_EQUAL (c, dconstm1))
- return fold (build2 (RDIV_EXPR, type,
- build_real (type, dconst1), arg0));
+ return fold_build2 (RDIV_EXPR, type,
+ build_real (type, dconst1), arg0);
/* Optimize pow(x,0.5) = sqrt(x). */
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));
+ arg = fold_build2 (MULT_EXPR, type, arg, arg1);
arglist = build_tree_list (NULL_TREE, arg);
return build_function_call_expr (expfn, arglist);
}
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)));
+ tree narg1 = fold_build2 (MULT_EXPR, type, arg1,
+ build_real (type, dconsthalf));
arglist = tree_cons (NULL_TREE, narg0,
build_tree_list (NULL_TREE, narg1));
{
const REAL_VALUE_TYPE dconstroot
= real_value_truncate (TYPE_MODE (type), dconstthird);
- tree narg1 = fold (build2 (MULT_EXPR, type, arg1,
- build_real (type, dconstroot)));
+ 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);
{
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));
+ tree narg1 = fold_build2 (MULT_EXPR, type, arg01, arg1);
arglist = tree_cons (NULL_TREE, arg00,
build_tree_list (NULL_TREE, narg1));
return build_function_call_expr (fndecl, arglist);
/* Optimize pow(x,-1) = 1.0/x. */
if (c == -1)
- return fold (build2 (RDIV_EXPR, type,
- build_real (type, dconst1), arg0));
+ return fold_build2 (RDIV_EXPR, type,
+ build_real (type, dconst1), arg0);
}
return NULL_TREE;
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;
}
return omit_one_operand (type, dest, len);
if (endp == 2)
- len = fold (build2 (MINUS_EXPR, TREE_TYPE (len), len,
- ssize_int (1)));
+ len = fold_build2 (MINUS_EXPR, TREE_TYPE (len), len,
+ ssize_int (1));
len = fold_convert (TREE_TYPE (dest), len);
- len = fold (build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len));
+ len = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len);
return fold_convert (type, len);
}
}
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));
}
if (host_integerp (len, 1) && tree_low_cst (len, 1) == 1)
{
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
+ tree cst_uchar_ptr_node
+ = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+
tree ind1 = fold_convert (integer_type_node,
build1 (INDIRECT_REF, cst_uchar_node,
fold_convert (cst_uchar_ptr_node,
build1 (INDIRECT_REF, cst_uchar_node,
fold_convert (cst_uchar_ptr_node,
arg2)));
- return fold (build2 (MINUS_EXPR, integer_type_node, ind1, ind2));
+ return fold_build2 (MINUS_EXPR, integer_type_node, ind1, ind2);
}
return 0;
if (p2 && *p2 == '\0')
{
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
+ tree cst_uchar_ptr_node
+ = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+
return fold_convert (integer_type_node,
build1 (INDIRECT_REF, cst_uchar_node,
fold_convert (cst_uchar_ptr_node,
if (p1 && *p1 == '\0')
{
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
+ tree cst_uchar_ptr_node
+ = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+
tree temp = fold_convert (integer_type_node,
build1 (INDIRECT_REF, cst_uchar_node,
fold_convert (cst_uchar_ptr_node,
arg2)));
- return fold (build1 (NEGATE_EXPR, integer_type_node, temp));
+ return fold_build1 (NEGATE_EXPR, integer_type_node, temp);
}
return 0;
&& tree_int_cst_sgn (len) == 1)
{
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
+ tree cst_uchar_ptr_node
+ = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+
return fold_convert (integer_type_node,
build1 (INDIRECT_REF, cst_uchar_node,
fold_convert (cst_uchar_ptr_node,
&& tree_int_cst_sgn (len) == 1)
{
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
+ tree cst_uchar_ptr_node
+ = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+
tree temp = fold_convert (integer_type_node,
build1 (INDIRECT_REF, cst_uchar_node,
fold_convert (cst_uchar_ptr_node,
arg2)));
- return fold (build1 (NEGATE_EXPR, integer_type_node, temp));
+ return fold_build1 (NEGATE_EXPR, integer_type_node, temp);
}
/* If len parameter is one, return an expression corresponding to
if (host_integerp (len, 1) && tree_low_cst (len, 1) == 1)
{
tree cst_uchar_node = build_type_variant (unsigned_char_type_node, 1, 0);
- tree cst_uchar_ptr_node = build_pointer_type (cst_uchar_node);
+ tree cst_uchar_ptr_node
+ = build_pointer_type_for_mode (cst_uchar_node, ptr_mode, true);
+
tree ind1 = fold_convert (integer_type_node,
build1 (INDIRECT_REF, cst_uchar_node,
fold_convert (cst_uchar_ptr_node,
build1 (INDIRECT_REF, cst_uchar_node,
fold_convert (cst_uchar_ptr_node,
arg2)));
- return fold (build2 (MINUS_EXPR, integer_type_node, ind1, ind2));
+ return fold_build2 (MINUS_EXPR, integer_type_node, ind1, ind2);
}
return 0;
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,
- build_real (TREE_TYPE (arg), dconst0)));
+ return fold_build2 (LT_EXPR, type, arg,
+ build_real (TREE_TYPE (arg), dconst0));
return NULL_TREE;
}
c1 = TREE_REAL_CST (arg1);
c2 = TREE_REAL_CST (arg2);
+ /* c1.sign := c2.sign. */
real_copysign (&c1, &c2);
return build_real (type, c1);
- c1.sign = c2.sign;
}
/* copysign(X, Y) is fabs(X) when Y is always non-negative.
Remember to evaluate Y for side-effects. */
if (tree_expr_nonnegative_p (arg2))
return omit_one_operand (type,
- fold (build1 (ABS_EXPR, type, arg1)),
+ fold_build1 (ABS_EXPR, type, arg1),
arg2);
/* Strip sign changing operations for the first argument. */
arg = build2 (BIT_AND_EXPR, integer_type_node, arg,
build_int_cst (NULL_TREE,
~ (unsigned HOST_WIDE_INT) 0x7f));
- arg = fold (build2 (EQ_EXPR, integer_type_node,
- arg, integer_zero_node));
+ arg = fold_build2 (EQ_EXPR, integer_type_node,
+ arg, integer_zero_node);
if (in_gimple_form && !TREE_CONSTANT (arg))
return NULL_TREE;
/* Transform toascii(c) -> (c & 0x7f). */
tree arg = TREE_VALUE (arglist);
- return fold (build2 (BIT_AND_EXPR, integer_type_node, arg,
- build_int_cst (NULL_TREE, 0x7f)));
+ return fold_build2 (BIT_AND_EXPR, integer_type_node, arg,
+ build_int_cst (NULL_TREE, 0x7f));
}
}
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));
- arg = build2 (LE_EXPR, integer_type_node, arg,
- build_int_cst (unsigned_type_node, 9));
- arg = fold (arg);
+ arg = fold_build2 (LE_EXPR, integer_type_node, arg,
+ build_int_cst (unsigned_type_node, 9));
if (in_gimple_form && !TREE_CONSTANT (arg))
return NULL_TREE;
else
arg = fold_convert (type, arg);
if (TREE_CODE (arg) == REAL_CST)
return fold_abs_const (arg, type);
- return fold (build1 (ABS_EXPR, type, arg));
+ return fold_build1 (ABS_EXPR, type, arg);
}
/* Fold a call to abs, labs, llabs or imaxabs. */
arg = fold_convert (type, arg);
if (TREE_CODE (arg) == INTEGER_CST)
return fold_abs_const (arg, type);
- return fold (build1 (ABS_EXPR, type, arg));
+ return fold_build1 (ABS_EXPR, type, arg);
}
/* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite.
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;
}
arg = builtin_save_expr (arg);
- return fold (build2 (UNORDERED_EXPR, type, arg, arg));
+ return fold_build2 (UNORDERED_EXPR, type, arg, arg);
default:
gcc_unreachable ();
{
if (!MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg0))))
return omit_two_operands (type, integer_zero_node, arg0, arg1);
- return fold (build2 (UNORDERED_EXPR, type, arg0, arg1));
+ return fold_build2 (UNORDERED_EXPR, type, arg0, arg1);
}
code = MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg0))) ? unordered_code
: ordered_code;
- return fold (build1 (TRUTH_NOT_EXPR, type,
- 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);
+ return fold_build1 (TRUTH_NOT_EXPR, type,
+ fold_build2 (code, type, arg0, arg1));
}
/* Used by constant folding to simplify calls to builtin functions. EXP is
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);
fcode = DECL_FUNCTION_CODE (fndecl);
switch (fcode)
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);
case BUILT_IN_STRLEN:
return fold_builtin_strlen (arglist);
- case BUILT_IN_FABS:
- case BUILT_IN_FABSF:
- case BUILT_IN_FABSL:
+ CASE_FLT_FN (BUILT_IN_FABS):
return fold_builtin_fabs (arglist, type);
case BUILT_IN_ABS:
case BUILT_IN_IMAXABS:
return fold_builtin_abs (arglist, type);
- case BUILT_IN_CONJ:
- case BUILT_IN_CONJF:
- case BUILT_IN_CONJL:
+ CASE_FLT_FN (BUILT_IN_CONJ):
if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE))
- return fold (build1 (CONJ_EXPR, type, TREE_VALUE (arglist)));
+ return fold_build1 (CONJ_EXPR, type, TREE_VALUE (arglist));
break;
- case BUILT_IN_CREAL:
- case BUILT_IN_CREALF:
- case BUILT_IN_CREALL:
+ CASE_FLT_FN (BUILT_IN_CREAL):
if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE))
- return non_lvalue (fold (build1 (REALPART_EXPR, type,
- TREE_VALUE (arglist))));
+ return non_lvalue (fold_build1 (REALPART_EXPR, type,
+ TREE_VALUE (arglist)));
break;
- case BUILT_IN_CIMAG:
- case BUILT_IN_CIMAGF:
- case BUILT_IN_CIMAGL:
+ CASE_FLT_FN (BUILT_IN_CIMAG):
if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE))
- return non_lvalue (fold (build1 (IMAGPART_EXPR, type,
- TREE_VALUE (arglist))));
+ return non_lvalue (fold_build1 (IMAGPART_EXPR, type,
+ TREE_VALUE (arglist)));
break;
- case BUILT_IN_CABS:
- case BUILT_IN_CABSF:
- case BUILT_IN_CABSL:
+ CASE_FLT_FN (BUILT_IN_CABS):
return fold_builtin_cabs (arglist, type);
- case BUILT_IN_SQRT:
- case BUILT_IN_SQRTF:
- case BUILT_IN_SQRTL:
+ CASE_FLT_FN (BUILT_IN_SQRT):
return fold_builtin_sqrt (arglist, type);
- case BUILT_IN_CBRT:
- case BUILT_IN_CBRTF:
- case BUILT_IN_CBRTL:
+ CASE_FLT_FN (BUILT_IN_CBRT):
return fold_builtin_cbrt (arglist, type);
- case BUILT_IN_SIN:
- case BUILT_IN_SINF:
- case BUILT_IN_SINL:
+ CASE_FLT_FN (BUILT_IN_SIN):
return fold_builtin_sin (arglist);
- case BUILT_IN_COS:
- case BUILT_IN_COSF:
- case BUILT_IN_COSL:
+ CASE_FLT_FN (BUILT_IN_COS):
return fold_builtin_cos (arglist, type, fndecl);
- case BUILT_IN_EXP:
- case BUILT_IN_EXPF:
- case BUILT_IN_EXPL:
+ CASE_FLT_FN (BUILT_IN_EXP):
return fold_builtin_exponent (fndecl, arglist, &dconste);
- case BUILT_IN_EXP2:
- case BUILT_IN_EXP2F:
- case BUILT_IN_EXP2L:
+ CASE_FLT_FN (BUILT_IN_EXP2):
return fold_builtin_exponent (fndecl, arglist, &dconst2);
- case BUILT_IN_EXP10:
- case BUILT_IN_EXP10F:
- case BUILT_IN_EXP10L:
- case BUILT_IN_POW10:
- case BUILT_IN_POW10F:
- case BUILT_IN_POW10L:
+ CASE_FLT_FN (BUILT_IN_EXP10):
+ CASE_FLT_FN (BUILT_IN_POW10):
return fold_builtin_exponent (fndecl, arglist, &dconst10);
- case BUILT_IN_LOG:
- case BUILT_IN_LOGF:
- case BUILT_IN_LOGL:
+ CASE_FLT_FN (BUILT_IN_LOG):
return fold_builtin_logarithm (fndecl, arglist, &dconste);
- case BUILT_IN_LOG2:
- case BUILT_IN_LOG2F:
- case BUILT_IN_LOG2L:
+ CASE_FLT_FN (BUILT_IN_LOG2):
return fold_builtin_logarithm (fndecl, arglist, &dconst2);
- case BUILT_IN_LOG10:
- case BUILT_IN_LOG10F:
- case BUILT_IN_LOG10L:
+ CASE_FLT_FN (BUILT_IN_LOG10):
return fold_builtin_logarithm (fndecl, arglist, &dconst10);
- case BUILT_IN_TAN:
- case BUILT_IN_TANF:
- case BUILT_IN_TANL:
+ CASE_FLT_FN (BUILT_IN_TAN):
return fold_builtin_tan (arglist);
- case BUILT_IN_ATAN:
- case BUILT_IN_ATANF:
- case BUILT_IN_ATANL:
+ CASE_FLT_FN (BUILT_IN_ATAN):
return fold_builtin_atan (arglist, type);
- case BUILT_IN_POW:
- case BUILT_IN_POWF:
- case BUILT_IN_POWL:
+ CASE_FLT_FN (BUILT_IN_POW):
return fold_builtin_pow (fndecl, arglist, type);
- case BUILT_IN_POWI:
- case BUILT_IN_POWIF:
- case BUILT_IN_POWIL:
+ CASE_FLT_FN (BUILT_IN_POWI):
return fold_builtin_powi (fndecl, arglist, type);
- case BUILT_IN_INF:
- case BUILT_IN_INFF:
- case BUILT_IN_INFL:
+ CASE_FLT_FN (BUILT_IN_INF):
+ case BUILT_IN_INFD32:
+ case BUILT_IN_INFD64:
+ case BUILT_IN_INFD128:
return fold_builtin_inf (type, true);
- case BUILT_IN_HUGE_VAL:
- case BUILT_IN_HUGE_VALF:
- case BUILT_IN_HUGE_VALL:
+ CASE_FLT_FN (BUILT_IN_HUGE_VAL):
return fold_builtin_inf (type, false);
- case BUILT_IN_NAN:
- case BUILT_IN_NANF:
- case BUILT_IN_NANL:
+ CASE_FLT_FN (BUILT_IN_NAN):
+ case BUILT_IN_NAND32:
+ case BUILT_IN_NAND64:
+ case BUILT_IN_NAND128:
return fold_builtin_nan (arglist, type, true);
- case BUILT_IN_NANS:
- case BUILT_IN_NANSF:
- case BUILT_IN_NANSL:
+ CASE_FLT_FN (BUILT_IN_NANS):
return fold_builtin_nan (arglist, type, false);
- 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:
- return fold_builtin_round (exp);
-
- case BUILT_IN_NEARBYINT:
- case BUILT_IN_NEARBYINTF:
- case BUILT_IN_NEARBYINTL:
- case BUILT_IN_RINT:
- case BUILT_IN_RINTF:
- case BUILT_IN_RINTL:
- return fold_trunc_transparent_mathfn (exp);
-
- 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);
-
- case BUILT_IN_LRINT:
- case BUILT_IN_LRINTF:
- case BUILT_IN_LRINTL:
- case BUILT_IN_LLRINT:
- case BUILT_IN_LLRINTF:
- case BUILT_IN_LLRINTL:
- return fold_fixed_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);
+ CASE_FLT_FN (BUILT_IN_FLOOR):
+ return fold_builtin_floor (fndecl, arglist);
+
+ CASE_FLT_FN (BUILT_IN_CEIL):
+ return fold_builtin_ceil (fndecl, arglist);
+
+ CASE_FLT_FN (BUILT_IN_TRUNC):
+ return fold_builtin_trunc (fndecl, arglist);
+
+ CASE_FLT_FN (BUILT_IN_ROUND):
+ return fold_builtin_round (fndecl, arglist);
+
+ CASE_FLT_FN (BUILT_IN_NEARBYINT):
+ CASE_FLT_FN (BUILT_IN_RINT):
+ return fold_trunc_transparent_mathfn (fndecl, arglist);
+
+ CASE_FLT_FN (BUILT_IN_LCEIL):
+ CASE_FLT_FN (BUILT_IN_LLCEIL):
+ CASE_FLT_FN (BUILT_IN_LFLOOR):
+ CASE_FLT_FN (BUILT_IN_LLFLOOR):
+ CASE_FLT_FN (BUILT_IN_LROUND):
+ CASE_FLT_FN (BUILT_IN_LLROUND):
+ return fold_builtin_int_roundingfn (fndecl, arglist);
+
+ CASE_FLT_FN (BUILT_IN_LRINT):
+ CASE_FLT_FN (BUILT_IN_LLRINT):
+ return fold_fixed_mathfn (fndecl, arglist);
+
+ CASE_INT_FN (BUILT_IN_FFS):
+ CASE_INT_FN (BUILT_IN_CLZ):
+ CASE_INT_FN (BUILT_IN_CTZ):
+ CASE_INT_FN (BUILT_IN_POPCOUNT):
+ CASE_INT_FN (BUILT_IN_PARITY):
+ 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_MEMMOVE:
return fold_builtin_memmove (arglist, type);
- case BUILT_IN_SIGNBIT:
- case BUILT_IN_SIGNBITF:
- case BUILT_IN_SIGNBITL:
- return fold_builtin_signbit (exp);
+ CASE_FLT_FN (BUILT_IN_SIGNBIT):
+ return fold_builtin_signbit (fndecl, arglist);
case BUILT_IN_ISASCII:
return fold_builtin_isascii (arglist);
case BUILT_IN_ISDIGIT:
return fold_builtin_isdigit (arglist);
- case BUILT_IN_COPYSIGN:
- case BUILT_IN_COPYSIGNF:
- case BUILT_IN_COPYSIGNL:
+ CASE_FLT_FN (BUILT_IN_COPYSIGN):
return fold_builtin_copysign (fndecl, arglist, type);
- case BUILT_IN_FINITE:
- case BUILT_IN_FINITEF:
- case BUILT_IN_FINITEL:
- return fold_builtin_classify (exp, BUILT_IN_FINITE);
+ CASE_FLT_FN (BUILT_IN_FINITE):
+ case BUILT_IN_FINITED32:
+ case BUILT_IN_FINITED64:
+ case BUILT_IN_FINITED128:
+ 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);
+ CASE_FLT_FN (BUILT_IN_ISINF):
+ case BUILT_IN_ISINFD32:
+ case BUILT_IN_ISINFD64:
+ case BUILT_IN_ISINFD128:
+ 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);
+ CASE_FLT_FN (BUILT_IN_ISNAN):
+ case BUILT_IN_ISNAND32:
+ case BUILT_IN_ISNAND64:
+ case BUILT_IN_ISNAND128:
+ return fold_builtin_classify (fndecl, arglist, BUILT_IN_ISNAN);
case BUILT_IN_ISGREATER:
return fold_builtin_unordered_cmp (fndecl, arglist, UNLE_EXPR, LE_EXPR);
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;
- }
+ case BUILT_IN_OBJECT_SIZE:
+ return fold_builtin_object_size (arglist);
+ case BUILT_IN_MEMCPY_CHK:
+ case BUILT_IN_MEMPCPY_CHK:
+ case BUILT_IN_MEMMOVE_CHK:
+ case BUILT_IN_MEMSET_CHK:
+ return fold_builtin_memory_chk (fndecl, arglist, NULL_TREE, ignore,
+ DECL_FUNCTION_CODE (fndecl));
+ case BUILT_IN_STRCPY_CHK:
+ case BUILT_IN_STPCPY_CHK:
+ return fold_builtin_stxcpy_chk (fndecl, arglist, NULL_TREE, ignore,
+ DECL_FUNCTION_CODE (fndecl));
+ case BUILT_IN_STRNCPY_CHK:
+ return fold_builtin_strncpy_chk (arglist, NULL_TREE);
+ case BUILT_IN_STRCAT_CHK:
+ return fold_builtin_strcat_chk (fndecl, arglist);
+ case BUILT_IN_STRNCAT_CHK:
+ return fold_builtin_strncat_chk (fndecl, arglist);
+ case BUILT_IN_SPRINTF_CHK:
+ case BUILT_IN_VSPRINTF_CHK:
+ return fold_builtin_sprintf_chk (arglist, DECL_FUNCTION_CODE (fndecl));
+ case BUILT_IN_SNPRINTF_CHK:
+ case BUILT_IN_VSNPRINTF_CHK:
+ return fold_builtin_snprintf_chk (arglist, NULL_TREE,
+ DECL_FUNCTION_CODE (fndecl));
+
+ case BUILT_IN_PRINTF:
+ case BUILT_IN_PRINTF_UNLOCKED:
+ case BUILT_IN_VPRINTF:
+ case BUILT_IN_PRINTF_CHK:
+ case BUILT_IN_VPRINTF_CHK:
+ return fold_builtin_printf (fndecl, arglist, ignore,
+ DECL_FUNCTION_CODE (fndecl));
+
+ case BUILT_IN_FPRINTF:
+ case BUILT_IN_FPRINTF_UNLOCKED:
+ case BUILT_IN_VFPRINTF:
+ case BUILT_IN_FPRINTF_CHK:
+ case BUILT_IN_VFPRINTF_CHK:
+ return fold_builtin_fprintf (fndecl, arglist, ignore,
+ DECL_FUNCTION_CODE (fndecl));
+
+ default:
+ break;
+ }
return 0;
}
call node earlier than the warning is generated. */
tree
-fold_builtin (tree exp, bool ignore)
+fold_builtin (tree fndecl, tree arglist, bool ignore)
{
- exp = fold_builtin_1 (exp, ignore);
+ tree exp = fold_builtin_1 (fndecl, arglist, ignore);
if (exp)
{
- /* ??? Don't clobber shared nodes such as integer_zero_node. */
- if (CONSTANT_CLASS_P (exp))
- exp = build1 (NOP_EXPR, TREE_TYPE (exp), exp);
+ exp = build1 (NOP_EXPR, TREE_TYPE (exp), exp);
TREE_NO_WARNING (exp) = 1;
}
tree call_expr;
call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
- call_expr = build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
+ return fold_build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
call_expr, arglist, NULL_TREE);
- return fold (call_expr);
}
/* This function validates the types of a function call argument list
return NULL_RTX;
}
+/* Default target-specific library builtin expander that does nothing. */
+
+rtx
+default_expand_library_builtin (tree exp ATTRIBUTE_UNUSED,
+ rtx target ATTRIBUTE_UNUSED,
+ rtx subtarget ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ int ignore ATTRIBUTE_UNUSED)
+{
+ return NULL_RTX;
+}
+
/* Returns true is EXP represents data that would potentially reside
in a readonly section. */
return build_int_cst (TREE_TYPE (s1), 0);
/* Return an offset into the constant string argument. */
- tem = 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);
}
+ /* The argument is const char *, and the result is char *, so we need
+ a type conversion here to avoid a warning. */
if (p2[0] == '\0')
- return s1;
+ return fold_convert (type, s1);
if (p2[1] != '\0')
return 0;
return build_int_cst (TREE_TYPE (s1), 0);
/* Return an offset into the constant string argument. */
- tem = 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;
return build_int_cst (TREE_TYPE (s1), 0);
/* Return an offset into the constant string argument. */
- tem = 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 build_int_cst (TREE_TYPE (s1), 0);
/* Return an offset into the constant string argument. */
- tem = 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);
}
const char *p = c_getstr (src);
/* If the requested length is zero, or the src parameter string
- length is zero, return the dst parameter. */
+ length is zero, return the dst parameter. */
if (integer_zerop (len) || (p && *p == '\0'))
return omit_two_operands (TREE_TYPE (dst), dst, src, len);
fold_builtin_fputs (tree arglist, bool ignore, bool unlocked, tree len)
{
tree fn;
- tree fn_fputc = unlocked ? implicit_built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
+ /* If we're using an unlocked function, assume the other unlocked
+ functions exist explicitly. */
+ tree const fn_fputc = unlocked ? built_in_decls[BUILT_IN_FPUTC_UNLOCKED]
: implicit_built_in_decls[BUILT_IN_FPUTC];
- tree fn_fwrite = unlocked ? implicit_built_in_decls[BUILT_IN_FWRITE_UNLOCKED]
+ tree const fn_fwrite = unlocked ? built_in_decls[BUILT_IN_FWRITE_UNLOCKED]
: implicit_built_in_decls[BUILT_IN_FWRITE];
- /* If the return value is used, or the replacement _DECL isn't
- initialized, don't do the transformation. */
- if (!ignore || !fn_fputc || !fn_fwrite)
+ /* If the return value is used, don't do the transformation. */
+ if (!ignore)
return 0;
/* Verify the arguments in the original call. */
gcc_unreachable ();
}
+ /* If the replacement _DECL isn't initialized, don't do the
+ transformation. */
+ if (!fn)
+ return 0;
+
/* These optimizations are only performed when the result is ignored,
hence there's no need to cast the result to integer_type_node. */
return build_function_call_expr (fn, arglist);
{
/* Evidently an out of date version of <stdarg.h>; can't validate
va_start's second argument, but can still work as intended. */
- warning ("%<__builtin_next_arg%> called without an argument");
+ 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,
'sprintf (dest, "%s", orig)'. */
if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, VOID_TYPE)
&& !validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, POINTER_TYPE,
- VOID_TYPE))
+ VOID_TYPE))
return NULL_TREE;
/* Get the destination string and the format specifier. */
call = NULL_TREE;
retval = NULL_TREE;
+ if (!init_target_chars())
+ return 0;
+
/* If the format doesn't contain % args or %%, use strcpy. */
- if (strchr (fmt_str, '%') == NULL)
+ if (strchr (fmt_str, target_percent) == NULL)
{
tree fn = implicit_built_in_decls[BUILT_IN_STRCPY];
}
/* If the format is "%s", use strcpy if the result isn't used. */
- else if (fmt_str && strcmp (fmt_str, "%s") == 0)
+ else if (fmt_str && strcmp (fmt_str, target_percent_s) == 0)
{
tree fn, orig;
fn = implicit_built_in_decls[BUILT_IN_STRCPY];
else
return call;
}
+
+/* Expand a call to __builtin_object_size. */
+
+rtx
+expand_builtin_object_size (tree exp)
+{
+ tree ost;
+ int object_size_type;
+ tree fndecl = get_callee_fndecl (exp);
+ tree arglist = TREE_OPERAND (exp, 1);
+ location_t locus = EXPR_LOCATION (exp);
+
+ if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ {
+ error ("%Hfirst argument of %D must be a pointer, second integer constant",
+ &locus, fndecl);
+ expand_builtin_trap ();
+ return const0_rtx;
+ }
+
+ ost = TREE_VALUE (TREE_CHAIN (arglist));
+ STRIP_NOPS (ost);
+
+ if (TREE_CODE (ost) != INTEGER_CST
+ || tree_int_cst_sgn (ost) < 0
+ || compare_tree_int (ost, 3) > 0)
+ {
+ error ("%Hlast argument of %D is not integer constant between 0 and 3",
+ &locus, fndecl);
+ expand_builtin_trap ();
+ return const0_rtx;
+ }
+
+ object_size_type = tree_low_cst (ost, 0);
+
+ return object_size_type < 2 ? constm1_rtx : const0_rtx;
+}
+
+/* Expand EXP, a call to the __mem{cpy,pcpy,move,set}_chk builtin.
+ FCODE is the BUILT_IN_* to use.
+ Return 0 if we failed; the caller should emit a normal call,
+ otherwise try to get the result in TARGET, if convenient (and in
+ mode MODE if that's convenient). */
+
+static rtx
+expand_builtin_memory_chk (tree exp, rtx target, enum machine_mode mode,
+ enum built_in_function fcode)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree dest, src, len, size;
+
+ if (!validate_arglist (arglist,
+ POINTER_TYPE,
+ fcode == BUILT_IN_MEMSET_CHK
+ ? INTEGER_TYPE : POINTER_TYPE,
+ INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ dest = TREE_VALUE (arglist);
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
+
+ if (! host_integerp (size, 1))
+ return 0;
+
+ if (host_integerp (len, 1) || integer_all_onesp (size))
+ {
+ tree fn;
+
+ if (! integer_all_onesp (size) && tree_int_cst_lt (size, len))
+ {
+ location_t locus = EXPR_LOCATION (exp);
+ warning (0, "%Hcall to %D will always overflow destination buffer",
+ &locus, get_callee_fndecl (exp));
+ return 0;
+ }
+
+ arglist = build_tree_list (NULL_TREE, len);
+ arglist = tree_cons (NULL_TREE, src, arglist);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+
+ fn = NULL_TREE;
+ /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
+ mem{cpy,pcpy,move,set} is available. */
+ switch (fcode)
+ {
+ case BUILT_IN_MEMCPY_CHK:
+ fn = built_in_decls[BUILT_IN_MEMCPY];
+ break;
+ case BUILT_IN_MEMPCPY_CHK:
+ fn = built_in_decls[BUILT_IN_MEMPCPY];
+ break;
+ case BUILT_IN_MEMMOVE_CHK:
+ fn = built_in_decls[BUILT_IN_MEMMOVE];
+ break;
+ case BUILT_IN_MEMSET_CHK:
+ fn = built_in_decls[BUILT_IN_MEMSET];
+ break;
+ default:
+ break;
+ }
+
+ if (! fn)
+ return 0;
+
+ 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);
+ }
+ else if (fcode == BUILT_IN_MEMSET_CHK)
+ return 0;
+ else
+ {
+ unsigned int dest_align
+ = get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
+
+ /* If DEST is not a pointer type, call the normal function. */
+ if (dest_align == 0)
+ return 0;
+
+ /* If SRC and DEST are the same (and not volatile), do nothing. */
+ if (operand_equal_p (src, dest, 0))
+ {
+ tree expr;
+
+ if (fcode != BUILT_IN_MEMPCPY_CHK)
+ {
+ /* Evaluate and ignore LEN in case it has side-effects. */
+ expand_expr (len, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ return expand_expr (dest, target, mode, EXPAND_NORMAL);
+ }
+
+ len = fold_convert (TREE_TYPE (dest), len);
+ expr = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len);
+ return expand_expr (expr, target, mode, EXPAND_NORMAL);
+ }
+
+ /* __memmove_chk special case. */
+ if (fcode == BUILT_IN_MEMMOVE_CHK)
+ {
+ unsigned int src_align
+ = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
+
+ if (src_align == 0)
+ return 0;
+
+ /* If src is categorized for a readonly section we can use
+ normal __memcpy_chk. */
+ if (readonly_data_expr (src))
+ {
+ tree fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
+ if (!fn)
+ return 0;
+ 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);
+ }
+ }
+ return 0;
+ }
+}
+
+/* Emit warning if a buffer overflow is detected at compile time. */
+
+static void
+maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
+{
+ int arg_mask, is_strlen = 0;
+ tree arglist = TREE_OPERAND (exp, 1), a;
+ tree len, size;
+ location_t locus;
+
+ switch (fcode)
+ {
+ case BUILT_IN_STRCPY_CHK:
+ case BUILT_IN_STPCPY_CHK:
+ /* For __strcat_chk the warning will be emitted only if overflowing
+ by at least strlen (dest) + 1 bytes. */
+ case BUILT_IN_STRCAT_CHK:
+ arg_mask = 6;
+ is_strlen = 1;
+ break;
+ case BUILT_IN_STRNCPY_CHK:
+ arg_mask = 12;
+ break;
+ case BUILT_IN_SNPRINTF_CHK:
+ case BUILT_IN_VSNPRINTF_CHK:
+ arg_mask = 10;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ len = NULL_TREE;
+ size = NULL_TREE;
+ for (a = arglist; a && arg_mask; a = TREE_CHAIN (a), arg_mask >>= 1)
+ if (arg_mask & 1)
+ {
+ if (len)
+ size = a;
+ else
+ len = a;
+ }
+
+ if (!len || !size)
+ return;
+
+ len = TREE_VALUE (len);
+ size = TREE_VALUE (size);
+
+ if (! host_integerp (size, 1) || integer_all_onesp (size))
+ return;
+
+ if (is_strlen)
+ {
+ len = c_strlen (len, 1);
+ if (! len || ! host_integerp (len, 1) || tree_int_cst_lt (len, size))
+ return;
+ }
+ else if (! host_integerp (len, 1) || ! tree_int_cst_lt (size, len))
+ return;
+
+ locus = EXPR_LOCATION (exp);
+ warning (0, "%Hcall to %D will always overflow destination buffer",
+ &locus, get_callee_fndecl (exp));
+}
+
+/* Emit warning if a buffer overflow is detected at compile time
+ in __sprintf_chk/__vsprintf_chk calls. */
+
+static void
+maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
+{
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree dest, size, len, fmt, flag;
+ const char *fmt_str;
+
+ /* Verify the required arguments in the original call. */
+ if (! arglist)
+ return;
+ dest = TREE_VALUE (arglist);
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return;
+ flag = TREE_VALUE (arglist);
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return;
+ size = TREE_VALUE (arglist);
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return;
+ fmt = TREE_VALUE (arglist);
+ arglist = TREE_CHAIN (arglist);
+
+ if (! host_integerp (size, 1) || integer_all_onesp (size))
+ return;
+
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return;
+
+ if (!init_target_chars())
+ return;
+
+ /* If the format doesn't contain % args or %%, we know its size. */
+ if (strchr (fmt_str, target_percent) == 0)
+ len = build_int_cstu (size_type_node, strlen (fmt_str));
+ /* If the format is "%s" and first ... argument is a string literal,
+ we know it too. */
+ else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, target_percent_s) == 0)
+ {
+ tree arg;
+
+ if (! arglist)
+ return;
+ arg = TREE_VALUE (arglist);
+ if (! POINTER_TYPE_P (TREE_TYPE (arg)))
+ return;
+
+ len = c_strlen (arg, 1);
+ if (!len || ! host_integerp (len, 1))
+ return;
+ }
+ else
+ return;
+
+ if (! tree_int_cst_lt (len, size))
+ {
+ location_t locus = EXPR_LOCATION (exp);
+ warning (0, "%Hcall to %D will always overflow destination buffer",
+ &locus, get_callee_fndecl (exp));
+ }
+}
+
+/* Fold a call to __builtin_object_size, if possible. */
+
+tree
+fold_builtin_object_size (tree arglist)
+{
+ tree ptr, ost, ret = 0;
+ int object_size_type;
+
+ if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ ptr = TREE_VALUE (arglist);
+ ost = TREE_VALUE (TREE_CHAIN (arglist));
+ STRIP_NOPS (ost);
+
+ if (TREE_CODE (ost) != INTEGER_CST
+ || tree_int_cst_sgn (ost) < 0
+ || compare_tree_int (ost, 3) > 0)
+ return 0;
+
+ object_size_type = tree_low_cst (ost, 0);
+
+ /* __builtin_object_size doesn't evaluate side-effects in its arguments;
+ if there are any side-effects, it returns (size_t) -1 for types 0 and 1
+ and (size_t) 0 for types 2 and 3. */
+ if (TREE_SIDE_EFFECTS (ptr))
+ return fold_convert (size_type_node,
+ object_size_type < 2
+ ? integer_minus_one_node : integer_zero_node);
+
+ if (TREE_CODE (ptr) == ADDR_EXPR)
+ ret = build_int_cstu (size_type_node,
+ compute_builtin_object_size (ptr, object_size_type));
+
+ else if (TREE_CODE (ptr) == SSA_NAME)
+ {
+ unsigned HOST_WIDE_INT bytes;
+
+ /* If object size is not known yet, delay folding until
+ later. Maybe subsequent passes will help determining
+ it. */
+ bytes = compute_builtin_object_size (ptr, object_size_type);
+ if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2
+ ? -1 : 0))
+ ret = build_int_cstu (size_type_node, bytes);
+ }
+
+ if (ret)
+ {
+ ret = force_fit_type (ret, -1, false, false);
+ if (TREE_CONSTANT_OVERFLOW (ret))
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/* Fold a call to the __mem{cpy,pcpy,move,set}_chk builtin.
+ IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_*
+ code of the builtin. If MAXLEN is not NULL, it is maximum length
+ passed as third argument. */
+
+tree
+fold_builtin_memory_chk (tree fndecl, tree arglist, tree maxlen, bool ignore,
+ enum built_in_function fcode)
+{
+ tree dest, src, len, size, fn;
+
+ if (!validate_arglist (arglist,
+ POINTER_TYPE,
+ fcode == BUILT_IN_MEMSET_CHK
+ ? INTEGER_TYPE : POINTER_TYPE,
+ INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ dest = TREE_VALUE (arglist);
+ /* Actually val for __memset_chk, but it doesn't matter. */
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
+
+ /* If SRC and DEST are the same (and not volatile), return DEST
+ (resp. DEST+LEN for __mempcpy_chk). */
+ if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
+ {
+ if (fcode != BUILT_IN_MEMPCPY_CHK)
+ return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len);
+ else
+ {
+ tree temp = fold_convert (TREE_TYPE (dest), len);
+ temp = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, temp);
+ return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), temp);
+ }
+ }
+
+ if (! host_integerp (size, 1))
+ return 0;
+
+ if (! integer_all_onesp (size))
+ {
+ if (! host_integerp (len, 1))
+ {
+ /* If LEN is not constant, try MAXLEN too.
+ For MAXLEN only allow optimizing into non-_ocs function
+ if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
+ if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
+ {
+ if (fcode == BUILT_IN_MEMPCPY_CHK && ignore)
+ {
+ /* (void) __mempcpy_chk () can be optimized into
+ (void) __memcpy_chk (). */
+ fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
+ if (!fn)
+ return 0;
+
+ return build_function_call_expr (fn, arglist);
+ }
+ return 0;
+ }
+ }
+ else
+ maxlen = len;
+
+ if (tree_int_cst_lt (size, maxlen))
+ return 0;
+ }
+
+ arglist = build_tree_list (NULL_TREE, len);
+ arglist = tree_cons (NULL_TREE, src, arglist);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+
+ fn = NULL_TREE;
+ /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume
+ mem{cpy,pcpy,move,set} is available. */
+ switch (fcode)
+ {
+ case BUILT_IN_MEMCPY_CHK:
+ fn = built_in_decls[BUILT_IN_MEMCPY];
+ break;
+ case BUILT_IN_MEMPCPY_CHK:
+ fn = built_in_decls[BUILT_IN_MEMPCPY];
+ break;
+ case BUILT_IN_MEMMOVE_CHK:
+ fn = built_in_decls[BUILT_IN_MEMMOVE];
+ break;
+ case BUILT_IN_MEMSET_CHK:
+ fn = built_in_decls[BUILT_IN_MEMSET];
+ break;
+ default:
+ break;
+ }
+
+ if (!fn)
+ return 0;
+
+ return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to the __st[rp]cpy_chk builtin.
+ IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_*
+ code of the builtin. If MAXLEN is not NULL, it is maximum length of
+ strings passed as second argument. */
+
+tree
+fold_builtin_stxcpy_chk (tree fndecl, tree arglist, tree maxlen, bool ignore,
+ enum built_in_function fcode)
+{
+ tree dest, src, size, len, fn;
+
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE,
+ VOID_TYPE))
+ return 0;
+
+ dest = TREE_VALUE (arglist);
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+ /* If SRC and DEST are the same (and not volatile), return DEST. */
+ if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
+ return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), dest);
+
+ if (! host_integerp (size, 1))
+ return 0;
+
+ if (! integer_all_onesp (size))
+ {
+ len = c_strlen (src, 1);
+ if (! len || ! host_integerp (len, 1))
+ {
+ /* If LEN is not constant, try MAXLEN too.
+ For MAXLEN only allow optimizing into non-_ocs function
+ if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
+ if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
+ {
+ if (fcode == BUILT_IN_STPCPY_CHK)
+ {
+ if (! ignore)
+ return 0;
+
+ /* If return value of __stpcpy_chk is ignored,
+ optimize into __strcpy_chk. */
+ fn = built_in_decls[BUILT_IN_STRCPY_CHK];
+ if (!fn)
+ return 0;
+
+ return build_function_call_expr (fn, arglist);
+ }
+
+ if (! len || TREE_SIDE_EFFECTS (len))
+ return 0;
+
+ /* If c_strlen returned something, but not a constant,
+ transform __strcpy_chk into __memcpy_chk. */
+ fn = built_in_decls[BUILT_IN_MEMCPY_CHK];
+ if (!fn)
+ return 0;
+
+ len = size_binop (PLUS_EXPR, len, ssize_int (1));
+ arglist = build_tree_list (NULL_TREE, size);
+ arglist = tree_cons (NULL_TREE, len, arglist);
+ arglist = tree_cons (NULL_TREE, src, arglist);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+ return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)),
+ build_function_call_expr (fn, arglist));
+ }
+ }
+ else
+ maxlen = len;
+
+ if (! tree_int_cst_lt (maxlen, size))
+ return 0;
+ }
+
+ arglist = build_tree_list (NULL_TREE, src);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+
+ /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available. */
+ fn = built_in_decls[fcode == BUILT_IN_STPCPY_CHK
+ ? BUILT_IN_STPCPY : BUILT_IN_STRCPY];
+ if (!fn)
+ return 0;
+
+ return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to the __strncpy_chk builtin.
+ If MAXLEN is not NULL, it is maximum length passed as third argument. */
+
+tree
+fold_builtin_strncpy_chk (tree arglist, tree maxlen)
+{
+ tree dest, src, size, len, fn;
+
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE,
+ INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ dest = TREE_VALUE (arglist);
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
+
+ if (! host_integerp (size, 1))
+ return 0;
+
+ if (! integer_all_onesp (size))
+ {
+ if (! host_integerp (len, 1))
+ {
+ /* If LEN is not constant, try MAXLEN too.
+ For MAXLEN only allow optimizing into non-_ocs function
+ if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
+ if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
+ return 0;
+ }
+ else
+ maxlen = len;
+
+ if (tree_int_cst_lt (size, maxlen))
+ return 0;
+ }
+
+ arglist = build_tree_list (NULL_TREE, len);
+ arglist = tree_cons (NULL_TREE, src, arglist);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+
+ /* If __builtin_strncpy_chk is used, assume strncpy is available. */
+ fn = built_in_decls[BUILT_IN_STRNCPY];
+ if (!fn)
+ return 0;
+
+ return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to the __strcat_chk builtin FNDECL with ARGLIST. */
+
+static tree
+fold_builtin_strcat_chk (tree fndecl, tree arglist)
+{
+ tree dest, src, size, fn;
+ const char *p;
+
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE,
+ VOID_TYPE))
+ return 0;
+
+ dest = TREE_VALUE (arglist);
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+ p = c_getstr (src);
+ /* If the SRC parameter is "", return DEST. */
+ if (p && *p == '\0')
+ return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src);
+
+ if (! host_integerp (size, 1) || ! integer_all_onesp (size))
+ return 0;
+
+ arglist = build_tree_list (NULL_TREE, src);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+
+ /* If __builtin_strcat_chk is used, assume strcat is available. */
+ fn = built_in_decls[BUILT_IN_STRCAT];
+ if (!fn)
+ return 0;
+
+ return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to the __strncat_chk builtin EXP. */
+
+static tree
+fold_builtin_strncat_chk (tree fndecl, tree arglist)
+{
+ tree dest, src, size, len, fn;
+ const char *p;
+
+ if (!validate_arglist (arglist, POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE,
+ INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ dest = TREE_VALUE (arglist);
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+ len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arglist))));
+
+ p = c_getstr (src);
+ /* If the SRC parameter is "" or if LEN is 0, return DEST. */
+ if (p && *p == '\0')
+ return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, len);
+ else if (integer_zerop (len))
+ return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src);
+
+ if (! host_integerp (size, 1))
+ return 0;
+
+ if (! integer_all_onesp (size))
+ {
+ tree src_len = c_strlen (src, 1);
+ if (src_len
+ && host_integerp (src_len, 1)
+ && host_integerp (len, 1)
+ && ! tree_int_cst_lt (len, src_len))
+ {
+ /* If LEN >= strlen (SRC), optimize into __strcat_chk. */
+ fn = built_in_decls[BUILT_IN_STRCAT_CHK];
+ if (!fn)
+ return 0;
+
+ arglist = build_tree_list (NULL_TREE, size);
+ arglist = tree_cons (NULL_TREE, src, arglist);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+ return build_function_call_expr (fn, arglist);
+ }
+ return 0;
+ }
+
+ arglist = build_tree_list (NULL_TREE, len);
+ arglist = tree_cons (NULL_TREE, src, arglist);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+
+ /* If __builtin_strncat_chk is used, assume strncat is available. */
+ fn = built_in_decls[BUILT_IN_STRNCAT];
+ if (!fn)
+ return 0;
+
+ return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to __{,v}sprintf_chk with argument list ARGLIST. Return 0 if
+ a normal call should be emitted rather than expanding the function
+ inline. FCODE is either BUILT_IN_SPRINTF_CHK or BUILT_IN_VSPRINTF_CHK. */
+
+static tree
+fold_builtin_sprintf_chk (tree arglist, enum built_in_function fcode)
+{
+ tree dest, size, len, fn, fmt, flag;
+ const char *fmt_str;
+
+ /* Verify the required arguments in the original call. */
+ if (! arglist)
+ return 0;
+ dest = TREE_VALUE (arglist);
+ if (! POINTER_TYPE_P (TREE_TYPE (dest)))
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return 0;
+ flag = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return 0;
+ size = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return 0;
+ fmt = TREE_VALUE (arglist);
+ if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+
+ if (! host_integerp (size, 1))
+ return 0;
+
+ len = NULL_TREE;
+
+ if (!init_target_chars())
+ return 0;
+
+ /* Check whether the format is a literal string constant. */
+ fmt_str = c_getstr (fmt);
+ if (fmt_str != NULL)
+ {
+ /* If the format doesn't contain % args or %%, we know the size. */
+ if (strchr (fmt_str, target_percent) == 0)
+ {
+ if (fcode != BUILT_IN_SPRINTF_CHK || arglist == NULL_TREE)
+ len = build_int_cstu (size_type_node, strlen (fmt_str));
+ }
+ /* If the format is "%s" and first ... argument is a string literal,
+ we know the size too. */
+ else if (fcode == BUILT_IN_SPRINTF_CHK && strcmp (fmt_str, target_percent_s) == 0)
+ {
+ tree arg;
+
+ if (arglist && !TREE_CHAIN (arglist))
+ {
+ arg = TREE_VALUE (arglist);
+ if (POINTER_TYPE_P (TREE_TYPE (arg)))
+ {
+ len = c_strlen (arg, 1);
+ if (! len || ! host_integerp (len, 1))
+ len = NULL_TREE;
+ }
+ }
+ }
+ }
+
+ if (! integer_all_onesp (size))
+ {
+ if (! len || ! tree_int_cst_lt (len, size))
+ return 0;
+ }
+
+ /* Only convert __{,v}sprintf_chk to {,v}sprintf if flag is 0
+ or if format doesn't contain % chars or is "%s". */
+ if (! integer_zerop (flag))
+ {
+ if (fmt_str == NULL)
+ return 0;
+ if (strchr (fmt_str, target_percent) != NULL && strcmp (fmt_str, target_percent_s))
+ return 0;
+ }
+
+ arglist = tree_cons (NULL_TREE, fmt, arglist);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+
+ /* If __builtin_{,v}sprintf_chk is used, assume {,v}sprintf is available. */
+ fn = built_in_decls[fcode == BUILT_IN_VSPRINTF_CHK
+ ? BUILT_IN_VSPRINTF : BUILT_IN_SPRINTF];
+ if (!fn)
+ return 0;
+
+ return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to {,v}snprintf with argument list ARGLIST. Return 0 if
+ a normal call should be emitted rather than expanding the function
+ inline. FCODE is either BUILT_IN_SNPRINTF_CHK or
+ BUILT_IN_VSNPRINTF_CHK. If MAXLEN is not NULL, it is maximum length
+ passed as second argument. */
+
+tree
+fold_builtin_snprintf_chk (tree arglist, tree maxlen,
+ enum built_in_function fcode)
+{
+ tree dest, size, len, fn, fmt, flag;
+ const char *fmt_str;
+
+ /* Verify the required arguments in the original call. */
+ if (! arglist)
+ return 0;
+ dest = TREE_VALUE (arglist);
+ if (! POINTER_TYPE_P (TREE_TYPE (dest)))
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return 0;
+ len = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return 0;
+ flag = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (len)) != INTEGER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return 0;
+ size = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE)
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+ if (! arglist)
+ return 0;
+ fmt = TREE_VALUE (arglist);
+ if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+
+ if (! host_integerp (size, 1))
+ return 0;
+
+ if (! integer_all_onesp (size))
+ {
+ if (! host_integerp (len, 1))
+ {
+ /* If LEN is not constant, try MAXLEN too.
+ For MAXLEN only allow optimizing into non-_ocs function
+ if SIZE is >= MAXLEN, never convert to __ocs_fail (). */
+ if (maxlen == NULL_TREE || ! host_integerp (maxlen, 1))
+ return 0;
+ }
+ else
+ maxlen = len;
+
+ if (tree_int_cst_lt (size, maxlen))
+ return 0;
+ }
+
+ if (!init_target_chars())
+ return 0;
+
+ /* Only convert __{,v}snprintf_chk to {,v}snprintf if flag is 0
+ or if format doesn't contain % chars or is "%s". */
+ if (! integer_zerop (flag))
+ {
+ fmt_str = c_getstr (fmt);
+ if (fmt_str == NULL)
+ return 0;
+ if (strchr (fmt_str, target_percent) != NULL && strcmp (fmt_str, target_percent_s))
+ return 0;
+ }
+
+ arglist = tree_cons (NULL_TREE, fmt, arglist);
+ arglist = tree_cons (NULL_TREE, len, arglist);
+ arglist = tree_cons (NULL_TREE, dest, arglist);
+
+ /* If __builtin_{,v}snprintf_chk is used, assume {,v}snprintf is
+ available. */
+ fn = built_in_decls[fcode == BUILT_IN_VSNPRINTF_CHK
+ ? BUILT_IN_VSNPRINTF : BUILT_IN_SNPRINTF];
+ if (!fn)
+ return 0;
+
+ return build_function_call_expr (fn, arglist);
+}
+
+/* Fold a call to the {,v}printf{,_unlocked} and __{,v}printf_chk builtins.
+
+ Return 0 if no simplification was possible, otherwise return the
+ simplified form of the call as a tree. FCODE is the BUILT_IN_*
+ code of the function to be simplified. */
+
+static tree
+fold_builtin_printf (tree fndecl, tree arglist, bool ignore,
+ enum built_in_function fcode)
+{
+ tree fmt, fn = NULL_TREE, fn_putchar, fn_puts, arg, call;
+ const char *fmt_str = NULL;
+
+ /* If the return value is used, don't do the transformation. */
+ if (! ignore)
+ return 0;
+
+ /* Verify the required arguments in the original call. */
+ if (fcode == BUILT_IN_PRINTF_CHK || fcode == BUILT_IN_VPRINTF_CHK)
+ {
+ tree flag;
+
+ if (! arglist)
+ return 0;
+ flag = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE
+ || TREE_SIDE_EFFECTS (flag))
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+ }
+
+ if (! arglist)
+ return 0;
+ fmt = TREE_VALUE (arglist);
+ if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
+ 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 NULL_TREE;
+
+ if (fcode == BUILT_IN_PRINTF_UNLOCKED)
+ {
+ /* If we're using an unlocked function, assume the other
+ unlocked functions exist explicitly. */
+ fn_putchar = built_in_decls[BUILT_IN_PUTCHAR_UNLOCKED];
+ fn_puts = built_in_decls[BUILT_IN_PUTS_UNLOCKED];
+ }
+ else
+ {
+ fn_putchar = implicit_built_in_decls[BUILT_IN_PUTCHAR];
+ fn_puts = implicit_built_in_decls[BUILT_IN_PUTS];
+ }
+
+ if (!init_target_chars())
+ return 0;
+
+ if (strcmp (fmt_str, target_percent_s) == 0 || strchr (fmt_str, target_percent) == NULL)
+ {
+ const char *str;
+
+ if (strcmp (fmt_str, target_percent_s) == 0)
+ {
+ if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
+ return 0;
+
+ if (! arglist
+ || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
+ || TREE_CHAIN (arglist))
+ return 0;
+
+ str = c_getstr (TREE_VALUE (arglist));
+ if (str == NULL)
+ return 0;
+ }
+ else
+ {
+ /* The format specifier doesn't contain any '%' characters. */
+ if (fcode != BUILT_IN_VPRINTF && fcode != BUILT_IN_VPRINTF_CHK
+ && arglist)
+ return 0;
+ str = fmt_str;
+ }
+
+ /* If the string was "", printf does nothing. */
+ if (str[0] == '\0')
+ return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0);
+
+ /* If the string has length of 1, call putchar. */
+ if (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_cst (NULL_TREE, str[0]);
+ arglist = build_tree_list (NULL_TREE, arg);
+ fn = fn_putchar;
+ }
+ else
+ {
+ /* If the string was "string\n", call puts("string"). */
+ size_t len = strlen (str);
+ if ((unsigned char)str[len - 1] == target_newline)
+ {
+ /* Create a NUL-terminated string that's one char shorter
+ than the original, stripping off the trailing '\n'. */
+ char *newstr = alloca (len);
+ memcpy (newstr, 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;
+ }
+ }
+
+ /* The other optimizations can be done only on the non-va_list variants. */
+ else if (fcode == BUILT_IN_VPRINTF || fcode == BUILT_IN_VPRINTF_CHK)
+ return 0;
+
+ /* If the format specifier was "%s\n", call __builtin_puts(arg). */
+ else if (strcmp (fmt_str, target_percent_s_newline) == 0)
+ {
+ if (! arglist
+ || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
+ || TREE_CHAIN (arglist))
+ return 0;
+ fn = fn_puts;
+ }
+
+ /* If the format specifier was "%c", call __builtin_putchar(arg). */
+ else if (strcmp (fmt_str, target_percent_c) == 0)
+ {
+ if (! arglist
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE
+ || TREE_CHAIN (arglist))
+ return 0;
+ fn = fn_putchar;
+ }
+
+ if (!fn)
+ return 0;
+
+ call = build_function_call_expr (fn, arglist);
+ return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), call);
+}
+
+/* Fold a call to the {,v}fprintf{,_unlocked} and __{,v}printf_chk builtins.
+
+ Return 0 if no simplification was possible, otherwise return the
+ simplified form of the call as a tree. FCODE is the BUILT_IN_*
+ code of the function to be simplified. */
+
+static tree
+fold_builtin_fprintf (tree fndecl, tree arglist, bool ignore,
+ enum built_in_function fcode)
+{
+ tree fp, fmt, fn = NULL_TREE, fn_fputc, fn_fputs, arg, call;
+ const char *fmt_str = NULL;
+
+ /* If the return value is used, don't do the transformation. */
+ if (! ignore)
+ return 0;
+
+ /* Verify the required arguments in the original call. */
+ if (! arglist)
+ return 0;
+ fp = TREE_VALUE (arglist);
+ if (! POINTER_TYPE_P (TREE_TYPE (fp)))
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+
+ if (fcode == BUILT_IN_FPRINTF_CHK || fcode == BUILT_IN_VFPRINTF_CHK)
+ {
+ tree flag;
+
+ if (! arglist)
+ return 0;
+ flag = TREE_VALUE (arglist);
+ if (TREE_CODE (TREE_TYPE (flag)) != INTEGER_TYPE
+ || TREE_SIDE_EFFECTS (flag))
+ return 0;
+ arglist = TREE_CHAIN (arglist);
+ }
+
+ if (! arglist)
+ return 0;
+ fmt = TREE_VALUE (arglist);
+ if (! POINTER_TYPE_P (TREE_TYPE (fmt)))
+ 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 NULL_TREE;
+
+ if (fcode == BUILT_IN_FPRINTF_UNLOCKED)
+ {
+ /* If we're using an unlocked function, assume the other
+ unlocked functions exist explicitly. */
+ fn_fputc = built_in_decls[BUILT_IN_FPUTC_UNLOCKED];
+ fn_fputs = built_in_decls[BUILT_IN_FPUTS_UNLOCKED];
+ }
+ else
+ {
+ fn_fputc = implicit_built_in_decls[BUILT_IN_FPUTC];
+ fn_fputs = implicit_built_in_decls[BUILT_IN_FPUTS];
+ }
+
+ if (!init_target_chars())
+ return 0;
+
+ /* If the format doesn't contain % args or %%, use strcpy. */
+ if (strchr (fmt_str, target_percent) == NULL)
+ {
+ if (fcode != BUILT_IN_VFPRINTF && fcode != BUILT_IN_VFPRINTF_CHK
+ && arglist)
+ return 0;
+
+ /* If the format specifier was "", fprintf does nothing. */
+ if (fmt_str[0] == '\0')
+ {
+ /* If FP has side-effects, just wait until gimplification is
+ done. */
+ if (TREE_SIDE_EFFECTS (fp))
+ return 0;
+
+ return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), 0);
+ }
+
+ /* When "string" doesn't contain %, replace all cases of
+ fprintf (fp, string) with fputs (string, fp). 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;
+ }
+
+ /* The other optimizations can be done only on the non-va_list variants. */
+ else if (fcode == BUILT_IN_VFPRINTF || fcode == BUILT_IN_VFPRINTF_CHK)
+ return 0;
+
+ /* If the format specifier was "%s", call __builtin_fputs (arg, fp). */
+ else if (strcmp (fmt_str, target_percent_s) == 0)
+ {
+ if (! arglist
+ || ! POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (arglist)))
+ || 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, target_percent_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;
+ }
+
+ if (!fn)
+ return 0;
+
+ call = build_function_call_expr (fn, arglist);
+ return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), call);
+}
+
+/* Initialize format string characters in the target charset. */
+
+static bool
+init_target_chars (void)
+{
+ static bool init;
+ if (!init)
+ {
+ target_newline = lang_hooks.to_target_charset ('\n');
+ target_percent = lang_hooks.to_target_charset ('%');
+ target_c = lang_hooks.to_target_charset ('c');
+ target_s = lang_hooks.to_target_charset ('s');
+ if (target_newline == 0 || target_percent == 0 || target_c == 0
+ || target_s == 0)
+ return false;
+
+ target_percent_c[0] = target_percent;
+ target_percent_c[1] = target_c;
+ target_percent_c[2] = '\0';
+
+ target_percent_s[0] = target_percent;
+ target_percent_s[1] = target_s;
+ target_percent_s[2] = '\0';
+
+ target_percent_s_newline[0] = target_percent;
+ target_percent_s_newline[1] = target_s;
+ target_percent_s_newline[2] = target_newline;
+ target_percent_s_newline[3] = '\0';
+
+ init = true;
+ }
+ return true;
+}