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_memory_op (tree, tree, bool, int);
static tree fold_builtin_strchr (tree, tree);
static tree fold_builtin_memcmp (tree);
static tree fold_builtin_strcmp (tree);
/* See what we are pointing at and look at its alignment. */
exp = TREE_OPERAND (exp, 0);
inner = max_align;
- while (handled_component_p (exp))
+ if (handled_component_p (exp))
{
- /* Fields in a structure can be packed, honor DECL_ALIGN
- of the FIELD_DECL. For all other references the conservative
- alignment is the element type alignment. */
- if (TREE_CODE (exp) == COMPONENT_REF)
- inner = MIN (inner, DECL_ALIGN (TREE_OPERAND (exp, 1)));
- else
- inner = MIN (inner, TYPE_ALIGN (TREE_TYPE (exp)));
- exp = TREE_OPERAND (exp, 0);
+ HOST_WIDE_INT bitsize, bitpos;
+ tree offset;
+ enum machine_mode mode;
+ int unsignedp, volatilep;
+
+ exp = get_inner_reference (exp, &bitsize, &bitpos, &offset,
+ &mode, &unsignedp, &volatilep, true);
+ if (bitpos)
+ inner = MIN (inner, (unsigned) (bitpos & -bitpos));
+ if (offset && TREE_CODE (offset) == PLUS_EXPR
+ && host_integerp (TREE_OPERAND (offset, 1), 1))
+ {
+ /* Any overflow in calculating offset_bits won't change
+ the alignment. */
+ unsigned offset_bits
+ = ((unsigned) tree_low_cst (TREE_OPERAND (offset, 1), 1)
+ * BITS_PER_UNIT);
+
+ if (offset_bits)
+ inner = MIN (inner, (offset_bits & -offset_bits));
+ offset = TREE_OPERAND (offset, 0);
+ }
+ if (offset && TREE_CODE (offset) == MULT_EXPR
+ && host_integerp (TREE_OPERAND (offset, 1), 1))
+ {
+ /* Any overflow in calculating offset_factor won't change
+ the alignment. */
+ unsigned offset_factor
+ = ((unsigned) tree_low_cst (TREE_OPERAND (offset, 1), 1)
+ * BITS_PER_UNIT);
+
+ if (offset_factor)
+ inner = MIN (inner, (offset_factor & -offset_factor));
+ }
+ else if (offset)
+ inner = MIN (inner, BITS_PER_UNIT);
}
if (TREE_CODE (exp) == FUNCTION_DECL)
align = FUNCTION_BOUNDARY;
else if (CONSTANT_CLASS_P (exp))
align = MIN (inner, (unsigned)CONSTANT_ALIGNMENT (exp, align));
#endif
+ else if (TREE_CODE (exp) == VIEW_CONVERT_EXPR
+ || TREE_CODE (exp) == INDIRECT_REF)
+ align = MIN (TYPE_ALIGN (TREE_TYPE (exp)), inner);
else
align = MIN (align, inner);
return MIN (align, max_align);
#else
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
+ /* For a zero count with __builtin_return_address, we don't care what
+ frame address we return, because target-specific definitions will
+ override us. Therefore frame pointer elimination is OK, and using
+ the soft frame pointer is OK.
+
+ For a non-zero count, or a zero count with __builtin_frame_address,
+ 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)
+ if (count == 0 && fndecl_code == BUILT_IN_RETURN_ADDRESS)
tem = frame_pointer_rtx;
else
{
emit_label (next_lab);
+ /* Because setjmp and longjmp are not represented in the CFG, a cfgcleanup
+ may find that the basic block starting with NEXT_LAB is unreachable.
+ The whole block, along with NEXT_LAB, would be removed (see PR26983).
+ Make sure that never happens. */
+ LABEL_PRESERVE_P (next_lab) = 1;
+
expand_builtin_setjmp_receiver (next_lab);
/* Set TARGET to one. */
unsigned int dest_align
= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
rtx dest_mem, src_mem, dest_addr, len_rtx;
- tree result = fold_builtin_memcpy (fndecl, arglist);
+ tree result = fold_builtin_memory_op (arglist, TREE_TYPE (TREE_TYPE (fndecl)),
+ false, /*endp=*/0);
if (result)
- return expand_expr (result, target, mode, EXPAND_NORMAL);
+ {
+ while (TREE_CODE (result) == COMPOUND_EXPR)
+ {
+ expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
+ EXPAND_NORMAL);
+ result = TREE_OPERAND (result, 1);
+ }
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
+ }
/* If DEST is not a pointer type, call the normal function. */
if (dest_align == 0)
unsigned int dest_align
= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
rtx dest_mem, src_mem, len_rtx;
- tree result = fold_builtin_mempcpy (arglist, type, endp);
+ tree result = fold_builtin_memory_op (arglist, type, false, endp);
if (result)
- return expand_expr (result, target, mode, EXPAND_NORMAL);
+ {
+ while (TREE_CODE (result) == COMPOUND_EXPR)
+ {
+ expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
+ EXPAND_NORMAL);
+ result = TREE_OPERAND (result, 1);
+ }
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
+ }
/* If either SRC or DEST is not a pointer type, don't do this
operation in-line. */
unsigned int src_align = get_pointer_alignment (src, BIGGEST_ALIGNMENT);
unsigned int dest_align
= get_pointer_alignment (dest, BIGGEST_ALIGNMENT);
- tree result = fold_builtin_memmove (arglist, type);
+ tree result = fold_builtin_memory_op (arglist, type, false, /*endp=*/3);
if (result)
- return expand_expr (result, target, mode, EXPAND_NORMAL);
+ {
+ while (TREE_CODE (result) == COMPOUND_EXPR)
+ {
+ expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
+ EXPAND_NORMAL);
+ result = TREE_OPERAND (result, 1);
+ }
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
+ }
/* If DEST is not a pointer type, call the normal function. */
if (dest_align == 0)
{
tree result = fold_builtin_strcpy (fndecl, arglist, 0);
if (result)
- return expand_expr (result, target, mode, EXPAND_NORMAL);
+ {
+ while (TREE_CODE (result) == COMPOUND_EXPR)
+ {
+ expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
+ EXPAND_NORMAL);
+ result = TREE_OPERAND (result, 1);
+ }
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
+ }
return expand_movstr (TREE_VALUE (arglist),
TREE_VALUE (TREE_CHAIN (arglist)),
tree result = fold_builtin_strncpy (fndecl, arglist, slen);
if (result)
- return expand_expr (result, target, mode, EXPAND_NORMAL);
+ {
+ while (TREE_CODE (result) == COMPOUND_EXPR)
+ {
+ expand_expr (TREE_OPERAND (result, 0), const0_rtx, VOIDmode,
+ EXPAND_NORMAL);
+ result = TREE_OPERAND (result, 1);
+ }
+ return expand_expr (result, target, mode, EXPAND_NORMAL);
+ }
/* We must be passed a constant len and src parameter. */
if (!host_integerp (len, 1) || !slen || !host_integerp (slen, 1))
return build_function_call_expr (decl, arglist);
}
}
+
+ /* Canonicalize llround (x) to lround (x) on LP64 targets where
+ sizeof (long long) == sizeof (long). */
+ if (TYPE_PRECISION (long_long_integer_type_node)
+ == TYPE_PRECISION (long_integer_type_node))
+ {
+ tree newfn = NULL_TREE;
+ switch (fcode)
+ {
+ CASE_FLT_FN (BUILT_IN_LLCEIL):
+ newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LCEIL);
+ break;
+
+ CASE_FLT_FN (BUILT_IN_LLFLOOR):
+ newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LFLOOR);
+ break;
+
+ CASE_FLT_FN (BUILT_IN_LLROUND):
+ newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LROUND);
+ break;
+
+ CASE_FLT_FN (BUILT_IN_LLRINT):
+ newfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_LRINT);
+ break;
+
+ default:
+ break;
+ }
+
+ if (newfn)
+ {
+ tree newcall = build_function_call_expr (newfn, arglist);
+ return fold_convert (TREE_TYPE (TREE_TYPE (fndecl)), newcall);
+ }
+ }
+
return 0;
}
return 0;
}
-/* Fold function call to builtin memcpy. Return
+/* Fold function call to builtin memset. Return
NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_memcpy (tree fndecl, tree arglist)
+fold_builtin_memset (tree arglist, tree type, bool ignore)
{
- tree dest, src, len;
+ tree dest, c, len, var, ret;
+ unsigned HOST_WIDE_INT length, cval;
if (!validate_arglist (arglist,
- POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ POINTER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
dest = TREE_VALUE (arglist);
- src = TREE_VALUE (TREE_CHAIN (arglist));
+ c = TREE_VALUE (TREE_CHAIN (arglist));
len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ if (! host_integerp (len, 1))
+ return 0;
+
/* If the LEN parameter is zero, return DEST. */
if (integer_zerop (len))
- return omit_one_operand (TREE_TYPE (TREE_TYPE (fndecl)), dest, src);
+ return omit_one_operand (type, dest, c);
- /* 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 (TREE_TYPE (fndecl)), dest, len);
+ if (! host_integerp (c, 1) || TREE_SIDE_EFFECTS (dest))
+ return 0;
- return 0;
+ var = dest;
+ STRIP_NOPS (var);
+ if (TREE_CODE (var) != ADDR_EXPR)
+ return 0;
+
+ var = TREE_OPERAND (var, 0);
+ if (TREE_THIS_VOLATILE (var))
+ return 0;
+
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (var))
+ && !POINTER_TYPE_P (TREE_TYPE (var)))
+ return 0;
+
+ length = tree_low_cst (len, 1);
+ if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (var))) != length
+ || get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT
+ < (int) length)
+ return 0;
+
+ if (length > HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT)
+ return 0;
+
+ if (integer_zerop (c))
+ cval = 0;
+ else
+ {
+ if (CHAR_BIT != 8 || BITS_PER_UNIT != 8 || HOST_BITS_PER_WIDE_INT > 64)
+ return 0;
+
+ cval = tree_low_cst (c, 1);
+ cval &= 0xff;
+ cval |= cval << 8;
+ cval |= cval << 16;
+ cval |= (cval << 31) << 1;
+ }
+
+ ret = build_int_cst_type (TREE_TYPE (var), cval);
+ ret = build2 (MODIFY_EXPR, TREE_TYPE (var), var, ret);
+ if (ignore)
+ return ret;
+
+ return omit_one_operand (type, dest, ret);
}
-/* Fold function call to builtin mempcpy. Return
+/* Fold function call to builtin memset. Return
NULL_TREE if no simplification can be made. */
static tree
-fold_builtin_mempcpy (tree arglist, tree type, int endp)
+fold_builtin_bzero (tree arglist, bool ignore)
{
- if (validate_arglist (arglist,
- POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
- {
- tree dest = TREE_VALUE (arglist);
- tree src = TREE_VALUE (TREE_CHAIN (arglist));
- tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ tree dest, size, newarglist;
- /* If the LEN parameter is zero, return DEST. */
- if (integer_zerop (len))
- return omit_one_operand (type, dest, src);
+ if (!validate_arglist (arglist, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
- /* If SRC and DEST are the same (and not volatile), return DEST+LEN. */
- if (operand_equal_p (src, dest, 0))
- {
- if (endp == 0)
- return omit_one_operand (type, dest, len);
+ if (!ignore)
+ return 0;
- if (endp == 2)
- len = fold_build2 (MINUS_EXPR, TREE_TYPE (len), len,
- ssize_int (1));
+ dest = TREE_VALUE (arglist);
+ size = TREE_VALUE (TREE_CHAIN (arglist));
- len = fold_convert (TREE_TYPE (dest), len);
- len = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len);
- return fold_convert (type, len);
- }
- }
- return 0;
+ /* New argument list transforming bzero(ptr x, int y) to
+ memset(ptr x, int 0, size_t y). This is done this way
+ so that if it isn't expanded inline, we fallback to
+ calling bzero instead of memset. */
+
+ newarglist = build_tree_list (NULL_TREE, fold_convert (sizetype, size));
+ newarglist = tree_cons (NULL_TREE, integer_zero_node, newarglist);
+ newarglist = tree_cons (NULL_TREE, dest, newarglist);
+ return fold_builtin_memset (newarglist, void_type_node, ignore);
}
-/* Fold function call to builtin memmove. Return
- NULL_TREE if no simplification can be made. */
+/* Fold function call to builtin mem{{,p}cpy,move}. Return
+ NULL_TREE if no simplification can be made.
+ If ENDP is 0, return DEST (like memcpy).
+ If ENDP is 1, return DEST+LEN (like mempcpy).
+ If ENDP is 2, return DEST+LEN-1 (like stpcpy).
+ If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap
+ (memmove). */
static tree
-fold_builtin_memmove (tree arglist, tree type)
+fold_builtin_memory_op (tree arglist, tree type, bool ignore, int endp)
{
- tree dest, src, len;
+ tree dest, src, len, destvar, srcvar, expr;
+ unsigned HOST_WIDE_INT length;
- if (!validate_arglist (arglist,
- POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ if (! validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
return 0;
dest = TREE_VALUE (arglist);
if (integer_zerop (len))
return omit_one_operand (type, dest, src);
- /* If SRC and DEST are the same (and not volatile), return DEST. */
+ /* If SRC and DEST are the same (and not volatile), return
+ DEST{,+LEN,+LEN-1}. */
if (operand_equal_p (src, dest, 0))
- return omit_one_operand (type, dest, len);
+ expr = len;
+ else
+ {
+ if (! host_integerp (len, 1))
+ return 0;
- return 0;
+ if (TREE_SIDE_EFFECTS (dest) || TREE_SIDE_EFFECTS (src))
+ return 0;
+
+ destvar = dest;
+ STRIP_NOPS (destvar);
+ if (TREE_CODE (destvar) != ADDR_EXPR)
+ return 0;
+
+ destvar = TREE_OPERAND (destvar, 0);
+ if (TREE_THIS_VOLATILE (destvar))
+ return 0;
+
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (destvar))
+ && !POINTER_TYPE_P (TREE_TYPE (destvar))
+ && !SCALAR_FLOAT_TYPE_P (TREE_TYPE (destvar)))
+ return 0;
+
+ srcvar = src;
+ STRIP_NOPS (srcvar);
+ if (TREE_CODE (srcvar) != ADDR_EXPR)
+ return 0;
+
+ srcvar = TREE_OPERAND (srcvar, 0);
+ if (TREE_THIS_VOLATILE (srcvar))
+ return 0;
+
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (srcvar))
+ && !POINTER_TYPE_P (TREE_TYPE (srcvar))
+ && !SCALAR_FLOAT_TYPE_P (TREE_TYPE (srcvar)))
+ return 0;
+
+ length = tree_low_cst (len, 1);
+ if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (destvar))) != length
+ || get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT
+ < (int) length
+ || GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (srcvar))) != length
+ || get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT
+ < (int) length)
+ return 0;
+
+ if ((INTEGRAL_TYPE_P (TREE_TYPE (srcvar))
+ || POINTER_TYPE_P (TREE_TYPE (srcvar)))
+ && (INTEGRAL_TYPE_P (TREE_TYPE (destvar))
+ || POINTER_TYPE_P (TREE_TYPE (destvar))))
+ expr = fold_convert (TREE_TYPE (destvar), srcvar);
+ else
+ expr = fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (destvar), srcvar);
+ expr = build2 (MODIFY_EXPR, TREE_TYPE (destvar), destvar, expr);
+ }
+
+ if (ignore)
+ return expr;
+
+ if (endp == 0 || endp == 3)
+ return omit_one_operand (type, dest, expr);
+
+ if (expr == len)
+ expr = 0;
+
+ if (endp == 2)
+ len = fold_build2 (MINUS_EXPR, TREE_TYPE (len), len,
+ ssize_int (1));
+
+ len = fold_convert (TREE_TYPE (dest), len);
+ dest = fold_build2 (PLUS_EXPR, TREE_TYPE (dest), dest, len);
+ dest = fold_convert (type, dest);
+ if (expr)
+ dest = omit_one_operand (type, dest, expr);
+ return dest;
+}
+
+/* Fold function call to builtin bcopy. Return NULL_TREE if no
+ simplification can be made. */
+
+static tree
+fold_builtin_bcopy (tree arglist, bool ignore)
+{
+ tree src, dest, size, newarglist;
+
+ if (!validate_arglist (arglist,
+ POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ if (! ignore)
+ return 0;
+
+ src = TREE_VALUE (arglist);
+ dest = TREE_VALUE (TREE_CHAIN (arglist));
+ size = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+ /* New argument list transforming bcopy(ptr x, ptr y, int z) to
+ memmove(ptr y, ptr x, size_t z). This is done this way
+ so that if it isn't expanded inline, we fallback to
+ calling bcopy instead of memmove. */
+
+ newarglist = build_tree_list (NULL_TREE, fold_convert (sizetype, size));
+ newarglist = tree_cons (NULL_TREE, src, newarglist);
+ newarglist = tree_cons (NULL_TREE, dest, newarglist);
+
+ return fold_builtin_memory_op (newarglist, void_type_node, true, /*endp=*/3);
}
/* Fold function call to builtin strcpy. If LEN is not NULL, it represents
CASE_INT_FN (BUILT_IN_PARITY):
return fold_builtin_bitop (fndecl, arglist);
+ case BUILT_IN_MEMSET:
+ return fold_builtin_memset (arglist, type, ignore);
+
case BUILT_IN_MEMCPY:
- return fold_builtin_memcpy (fndecl, arglist);
+ return fold_builtin_memory_op (arglist, type, ignore, /*endp=*/0);
case BUILT_IN_MEMPCPY:
- return fold_builtin_mempcpy (arglist, type, /*endp=*/1);
+ return fold_builtin_memory_op (arglist, type, ignore, /*endp=*/1);
case BUILT_IN_MEMMOVE:
- return fold_builtin_memmove (arglist, type);
+ return fold_builtin_memory_op (arglist, type, ignore, /*endp=*/3);
+
+ case BUILT_IN_BZERO:
+ return fold_builtin_bzero (arglist, ignore);
+
+ case BUILT_IN_BCOPY:
+ return fold_builtin_bcopy (arglist, ignore);
CASE_FLT_FN (BUILT_IN_SIGNBIT):
return fold_builtin_signbit (fndecl, arglist);