static tree fold_negate_const (tree, tree);
static tree fold_not_const (tree, tree);
static tree fold_relational_const (enum tree_code, tree, tree, tree);
-static bool tree_expr_nonzero_p (tree);
/* We know that A1 + B1 = SUM1, using 2's complement arithmetic and ignoring
overflow. Suppose A, B and SUM have the same respective signs as A1, B1,
}
}
\f
-/* Return false if expr can be assumed not to be an value, true
+/* Return false if expr can be assumed not to be an lvalue, true
otherwise. */
static bool
return fold_convert (type, integer_one_node);
if (low == 0)
- return fold_build2 (LE_EXPR, type, exp, high);
+ return fold_build2 (LE_EXPR, type, exp,
+ fold_convert (etype, high));
if (high == 0)
- return fold_build2 (GE_EXPR, type, exp, low);
+ return fold_build2 (GE_EXPR, type, exp,
+ fold_convert (etype, low));
if (operand_equal_p (low, high, 0))
- return fold_build2 (EQ_EXPR, type, exp, low);
+ return fold_build2 (EQ_EXPR, type, exp,
+ fold_convert (etype, low));
if (integer_zerop (low))
{
arg0 = op0;
if (arg0)
{
- if (code == NOP_EXPR || code == FLOAT_EXPR || code == CONVERT_EXPR)
+ if (code == NOP_EXPR || code == CONVERT_EXPR
+ || code == FLOAT_EXPR || code == ABS_EXPR)
{
- /* Don't use STRIP_NOPS, because signedness of argument type matters. */
+ /* Don't use STRIP_NOPS, because signedness of argument type
+ matters. */
STRIP_SIGN_NOPS (arg0);
}
else
return fold_build1 (code, type, TREE_OPERAND (op0, 0));
}
+ /* Handle (T *)&A.B.C for A being of type T and B and C
+ living at offset zero. This occurs frequently in
+ C++ upcasting and then accessing the base. */
+ if (TREE_CODE (op0) == ADDR_EXPR
+ && POINTER_TYPE_P (type)
+ && handled_component_p (TREE_OPERAND (op0, 0)))
+ {
+ HOST_WIDE_INT bitsize, bitpos;
+ tree offset;
+ enum machine_mode mode;
+ int unsignedp, volatilep;
+ tree base = TREE_OPERAND (op0, 0);
+ base = get_inner_reference (base, &bitsize, &bitpos, &offset,
+ &mode, &unsignedp, &volatilep, false);
+ /* If the reference was to a (constant) zero offset, we can use
+ the address of the base if it has the same base type
+ as the result type. */
+ if (! offset && bitpos == 0
+ && TYPE_MAIN_VARIANT (TREE_TYPE (type))
+ == TYPE_MAIN_VARIANT (TREE_TYPE (base)))
+ return fold_convert (type, build_fold_addr_expr (base));
+ }
+
if (TREE_CODE (op0) == MODIFY_EXPR
&& TREE_CONSTANT (TREE_OPERAND (op0, 1))
/* Detect assigning a bitfield. */
TREE_TYPE (targ0),
targ0));
}
- else if (tree_expr_nonnegative_p (arg0))
+ /* ABS_EXPR<ABS_EXPR<x>> = ABS_EXPR<x> even if flag_wrapv is on. */
+ else if (tree_expr_nonnegative_p (arg0) || TREE_CODE (arg0) == ABS_EXPR)
return arg0;
/* Strip sign ops from argument. */
goto bit_ior;
}
+ /* (X | Y) ^ X -> Y & ~ X*/
+ if (TREE_CODE (arg0) == BIT_IOR_EXPR
+ && operand_equal_p (TREE_OPERAND (arg0, 0), arg1, 0))
+ {
+ tree t2 = TREE_OPERAND (arg0, 1);
+ t1 = fold_build1 (BIT_NOT_EXPR, TREE_TYPE (arg1),
+ arg1);
+ t1 = fold_build2 (BIT_AND_EXPR, type, fold_convert (type, t2),
+ fold_convert (type, t1));
+ return t1;
+ }
+
+ /* (Y | X) ^ X -> Y & ~ X*/
+ if (TREE_CODE (arg0) == BIT_IOR_EXPR
+ && operand_equal_p (TREE_OPERAND (arg0, 1), arg1, 0))
+ {
+ tree t2 = TREE_OPERAND (arg0, 0);
+ t1 = fold_build1 (BIT_NOT_EXPR, TREE_TYPE (arg1),
+ arg1);
+ t1 = fold_build2 (BIT_AND_EXPR, type, fold_convert (type, t2),
+ fold_convert (type, t1));
+ return t1;
+ }
+
+ /* X ^ (X | Y) -> Y & ~ X*/
+ if (TREE_CODE (arg1) == BIT_IOR_EXPR
+ && operand_equal_p (TREE_OPERAND (arg1, 0), arg0, 0))
+ {
+ tree t2 = TREE_OPERAND (arg1, 1);
+ t1 = fold_build1 (BIT_NOT_EXPR, TREE_TYPE (arg0),
+ arg0);
+ t1 = fold_build2 (BIT_AND_EXPR, type, fold_convert (type, t2),
+ fold_convert (type, t1));
+ return t1;
+ }
+
+ /* X ^ (Y | X) -> Y & ~ X*/
+ if (TREE_CODE (arg1) == BIT_IOR_EXPR
+ && operand_equal_p (TREE_OPERAND (arg1, 1), arg0, 0))
+ {
+ tree t2 = TREE_OPERAND (arg1, 0);
+ t1 = fold_build1 (BIT_NOT_EXPR, TREE_TYPE (arg0),
+ arg0);
+ t1 = fold_build2 (BIT_AND_EXPR, type, fold_convert (type, t2),
+ fold_convert (type, t1));
+ return t1;
+ }
+
/* Convert ~X ^ ~Y to X ^ Y. */
if (TREE_CODE (arg0) == BIT_NOT_EXPR
&& TREE_CODE (arg1) == BIT_NOT_EXPR)
&& !TREE_SIDE_EFFECTS (arg1))
{
tem = fold_to_nonsharp_ineq_using_bound (arg0, arg1);
- if (tem)
+ if (tem && !operand_equal_p (tem, arg0, 0))
return fold_build2 (code, type, tem, arg1);
tem = fold_to_nonsharp_ineq_using_bound (arg1, arg0);
- if (tem)
+ if (tem && !operand_equal_p (tem, arg1, 0))
return fold_build2 (code, type, arg0, tem);
}
object against zero, then we know the result. */
if ((code == EQ_EXPR || code == NE_EXPR)
&& TREE_CODE (arg0) == ADDR_EXPR
- && DECL_P (TREE_OPERAND (arg0, 0))
+ && VAR_OR_FUNCTION_DECL_P (TREE_OPERAND (arg0, 0))
&& ! DECL_WEAK (TREE_OPERAND (arg0, 0))
&& integer_zerop (arg1))
return constant_boolean_node (code != EQ_EXPR, type);
have access to attributes for externs), then we know the result. */
if ((code == EQ_EXPR || code == NE_EXPR)
&& TREE_CODE (arg0) == ADDR_EXPR
- && DECL_P (TREE_OPERAND (arg0, 0))
+ && VAR_OR_FUNCTION_DECL_P (TREE_OPERAND (arg0, 0))
&& ! DECL_WEAK (TREE_OPERAND (arg0, 0))
&& ! lookup_attribute ("alias",
DECL_ATTRIBUTES (TREE_OPERAND (arg0, 0)))
&& ! DECL_EXTERNAL (TREE_OPERAND (arg0, 0))
&& TREE_CODE (arg1) == ADDR_EXPR
- && DECL_P (TREE_OPERAND (arg1, 0))
+ && VAR_OR_FUNCTION_DECL_P (TREE_OPERAND (arg1, 0))
&& ! DECL_WEAK (TREE_OPERAND (arg1, 0))
&& ! lookup_attribute ("alias",
DECL_ATTRIBUTES (TREE_OPERAND (arg1, 0)))
&& ! DECL_EXTERNAL (TREE_OPERAND (arg1, 0)))
- return constant_boolean_node (operand_equal_p (arg0, arg1, 0)
- ? code == EQ_EXPR : code != EQ_EXPR,
- type);
+ {
+ /* We know that we're looking at the address of two
+ non-weak, unaliased, static _DECL nodes.
+
+ It is both wasteful and incorrect to call operand_equal_p
+ to compare the two ADDR_EXPR nodes. It is wasteful in that
+ all we need to do is test pointer equality for the arguments
+ to the two ADDR_EXPR nodes. It is incorrect to use
+ operand_equal_p as that function is NOT equivalent to a
+ C equality test. It can in fact return false for two
+ objects which would test as equal using the C equality
+ operator. */
+ bool equal = TREE_OPERAND (arg0, 0) == TREE_OPERAND (arg1, 0);
+ return constant_boolean_node (equal
+ ? code == EQ_EXPR : code != EQ_EXPR,
+ type);
+ }
/* If this is a comparison of two exprs that look like an
ARRAY_REF of the same object, then we can fold this to a
}
}
+ /* Transform comparisons of the form X +- C1 CMP C2 to X CMP C2 +- C1. */
+ if ((TREE_CODE (arg0) == PLUS_EXPR || TREE_CODE (arg0) == MINUS_EXPR)
+ && (TREE_CODE (TREE_OPERAND (arg0, 1)) == INTEGER_CST
+ && !TREE_OVERFLOW (TREE_OPERAND (arg0, 1))
+ && !TYPE_UNSIGNED (TREE_TYPE (arg1))
+ && !(flag_wrapv || flag_trapv))
+ && (TREE_CODE (arg1) == INTEGER_CST
+ && !TREE_OVERFLOW (arg1)))
+ {
+ tree const1 = TREE_OPERAND (arg0, 1);
+ tree const2 = arg1;
+ tree variable = TREE_OPERAND (arg0, 0);
+ tree lhs;
+ int lhs_add;
+ lhs_add = TREE_CODE (arg0) != PLUS_EXPR;
+
+ lhs = fold_build2 (lhs_add ? PLUS_EXPR : MINUS_EXPR,
+ TREE_TYPE (arg1), const2, const1);
+ if (TREE_CODE (lhs) == TREE_CODE (arg1)
+ && (TREE_CODE (lhs) != INTEGER_CST
+ || !TREE_OVERFLOW (lhs)))
+ return fold_build2 (code, type, variable, lhs);
+ }
+
if (FLOAT_TYPE_P (TREE_TYPE (arg0)))
{
tree targ0 = strip_float_extensions (arg0);
}
if ((code == EQ_EXPR || code == NE_EXPR)
- && !TREE_SIDE_EFFECTS (arg0)
&& integer_zerop (arg1)
&& tree_expr_nonzero_p (arg0))
- return constant_boolean_node (code==NE_EXPR, type);
+ {
+ tree res = constant_boolean_node (code==NE_EXPR, type);
+ return omit_one_operand (type, res, arg0);
+ }
t1 = fold_relational_const (code, type, arg0, arg1);
return t1 == NULL_TREE ? NULL_TREE : t1;
if (TREE_CODE (arg0) == CONSTRUCTOR
&& ! type_contains_placeholder_p (TREE_TYPE (arg0)))
{
- tree m = purpose_member (arg1, CONSTRUCTOR_ELTS (arg0));
- if (m)
- return TREE_VALUE (m);
+ unsigned HOST_WIDE_INT idx;
+ tree field, value;
+ FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (arg0), idx, field, value)
+ if (field == arg1)
+ return value;
}
return NULL_TREE;
{
void **slot;
enum tree_code code;
- char buf[sizeof (struct tree_decl)];
+ char buf[sizeof (struct tree_decl_non_common)];
int i, len;
recursive_label:
gcc_assert ((sizeof (struct tree_exp) + 5 * sizeof (tree)
- <= sizeof (struct tree_decl))
- && sizeof (struct tree_type) <= sizeof (struct tree_decl));
+ <= sizeof (struct tree_decl_non_common))
+ && sizeof (struct tree_type) <= sizeof (struct tree_decl_non_common));
if (expr == NULL)
return;
slot = htab_find_slot (ht, expr, INSERT);
operand OP0. */
tree
-fold_build1 (enum tree_code code, tree type, tree op0)
+fold_build1_stat (enum tree_code code, tree type, tree op0 MEM_STAT_DECL)
{
- tree tem = fold_unary (code, type, op0);
- if (tem)
- return tem;
+ tree tem;
+#ifdef ENABLE_FOLD_CHECKING
+ unsigned char checksum_before[16], checksum_after[16];
+ struct md5_ctx ctx;
+ htab_t ht;
+
+ ht = htab_create (32, htab_hash_pointer, htab_eq_pointer, NULL);
+ md5_init_ctx (&ctx);
+ fold_checksum_tree (op0, &ctx, ht);
+ md5_finish_ctx (&ctx, checksum_before);
+ htab_empty (ht);
+#endif
+
+ tem = fold_unary (code, type, op0);
+ if (!tem)
+ tem = build1_stat (code, type, op0 PASS_MEM_STAT);
+
+#ifdef ENABLE_FOLD_CHECKING
+ md5_init_ctx (&ctx);
+ fold_checksum_tree (op0, &ctx, ht);
+ md5_finish_ctx (&ctx, checksum_after);
+ htab_delete (ht);
- return build1 (code, type, op0);
+ if (memcmp (checksum_before, checksum_after, 16))
+ fold_check_failed (op0, tem);
+#endif
+ return tem;
}
/* Fold a binary tree expression with code CODE of type TYPE with
with operands OP0 and OP1. */
tree
-fold_build2 (enum tree_code code, tree type, tree op0, tree op1)
+fold_build2_stat (enum tree_code code, tree type, tree op0, tree op1
+ MEM_STAT_DECL)
{
- tree tem = fold_binary (code, type, op0, op1);
- if (tem)
- return tem;
+ tree tem;
+#ifdef ENABLE_FOLD_CHECKING
+ unsigned char checksum_before_op0[16],
+ checksum_before_op1[16],
+ checksum_after_op0[16],
+ checksum_after_op1[16];
+ struct md5_ctx ctx;
+ htab_t ht;
- return build2 (code, type, op0, op1);
+ ht = htab_create (32, htab_hash_pointer, htab_eq_pointer, NULL);
+ md5_init_ctx (&ctx);
+ fold_checksum_tree (op0, &ctx, ht);
+ md5_finish_ctx (&ctx, checksum_before_op0);
+ htab_empty (ht);
+
+ md5_init_ctx (&ctx);
+ fold_checksum_tree (op1, &ctx, ht);
+ md5_finish_ctx (&ctx, checksum_before_op1);
+ htab_empty (ht);
+#endif
+
+ tem = fold_binary (code, type, op0, op1);
+ if (!tem)
+ tem = build2_stat (code, type, op0, op1 PASS_MEM_STAT);
+
+#ifdef ENABLE_FOLD_CHECKING
+ md5_init_ctx (&ctx);
+ fold_checksum_tree (op0, &ctx, ht);
+ md5_finish_ctx (&ctx, checksum_after_op0);
+ htab_empty (ht);
+
+ if (memcmp (checksum_before_op0, checksum_after_op0, 16))
+ fold_check_failed (op0, tem);
+
+ md5_init_ctx (&ctx);
+ fold_checksum_tree (op1, &ctx, ht);
+ md5_finish_ctx (&ctx, checksum_after_op1);
+ htab_delete (ht);
+
+ if (memcmp (checksum_before_op1, checksum_after_op1, 16))
+ fold_check_failed (op1, tem);
+#endif
+ return tem;
}
/* Fold a ternary tree expression with code CODE of type TYPE with
type TYPE with operands OP0, OP1, and OP2. */
tree
-fold_build3 (enum tree_code code, tree type, tree op0, tree op1, tree op2)
+fold_build3_stat (enum tree_code code, tree type, tree op0, tree op1, tree op2
+ MEM_STAT_DECL)
{
- tree tem = fold_ternary (code, type, op0, op1, op2);
- if (tem)
- return tem;
+ tree tem;
+#ifdef ENABLE_FOLD_CHECKING
+ unsigned char checksum_before_op0[16],
+ checksum_before_op1[16],
+ checksum_before_op2[16],
+ checksum_after_op0[16],
+ checksum_after_op1[16],
+ checksum_after_op2[16];
+ struct md5_ctx ctx;
+ htab_t ht;
+
+ ht = htab_create (32, htab_hash_pointer, htab_eq_pointer, NULL);
+ md5_init_ctx (&ctx);
+ fold_checksum_tree (op0, &ctx, ht);
+ md5_finish_ctx (&ctx, checksum_before_op0);
+ htab_empty (ht);
+
+ md5_init_ctx (&ctx);
+ fold_checksum_tree (op1, &ctx, ht);
+ md5_finish_ctx (&ctx, checksum_before_op1);
+ htab_empty (ht);
+
+ md5_init_ctx (&ctx);
+ fold_checksum_tree (op2, &ctx, ht);
+ md5_finish_ctx (&ctx, checksum_before_op2);
+ htab_empty (ht);
+#endif
+
+ tem = fold_ternary (code, type, op0, op1, op2);
+ if (!tem)
+ tem = build3_stat (code, type, op0, op1, op2 PASS_MEM_STAT);
+
+#ifdef ENABLE_FOLD_CHECKING
+ md5_init_ctx (&ctx);
+ fold_checksum_tree (op0, &ctx, ht);
+ md5_finish_ctx (&ctx, checksum_after_op0);
+ htab_empty (ht);
+
+ if (memcmp (checksum_before_op0, checksum_after_op0, 16))
+ fold_check_failed (op0, tem);
+
+ md5_init_ctx (&ctx);
+ fold_checksum_tree (op1, &ctx, ht);
+ md5_finish_ctx (&ctx, checksum_after_op1);
+ htab_empty (ht);
+
+ if (memcmp (checksum_before_op1, checksum_after_op1, 16))
+ fold_check_failed (op1, tem);
+
+ md5_init_ctx (&ctx);
+ fold_checksum_tree (op2, &ctx, ht);
+ md5_finish_ctx (&ctx, checksum_after_op2);
+ htab_delete (ht);
- return build3 (code, type, op0, op1, op2);
+ if (memcmp (checksum_before_op2, checksum_after_op2, 16))
+ fold_check_failed (op2, tem);
+#endif
+ return tem;
}
/* Perform constant folding and related simplification of initializer
int
tree_expr_nonnegative_p (tree t)
{
+ if (TYPE_UNSIGNED (TREE_TYPE (t)))
+ return 1;
+
switch (TREE_CODE (t))
{
case ABS_EXPR:
- return 1;
+ /* We can't return 1 if flag_wrapv is set because
+ ABS_EXPR<INT_MIN> = INT_MIN. */
+ if (!(flag_wrapv && INTEGRAL_TYPE_P (TREE_TYPE (t))))
+ return 1;
+ break;
case INTEGER_CST:
return tree_int_cst_sgn (t) >= 0;
CASE_BUILTIN_F (BUILT_IN_EXP2)
CASE_BUILTIN_F (BUILT_IN_FABS)
CASE_BUILTIN_F (BUILT_IN_FDIM)
- CASE_BUILTIN_F (BUILT_IN_FREXP)
CASE_BUILTIN_F (BUILT_IN_HYPOT)
CASE_BUILTIN_F (BUILT_IN_POW10)
CASE_BUILTIN_I (BUILT_IN_FFS)
CASE_BUILTIN_F (BUILT_IN_EXPM1)
CASE_BUILTIN_F (BUILT_IN_FLOOR)
CASE_BUILTIN_F (BUILT_IN_FMOD)
+ CASE_BUILTIN_F (BUILT_IN_FREXP)
CASE_BUILTIN_F (BUILT_IN_LCEIL)
CASE_BUILTIN_F (BUILT_IN_LDEXP)
CASE_BUILTIN_F (BUILT_IN_LFLOOR)
For floating point we further ensure that T is not denormal.
Similar logic is present in nonzero_address in rtlanal.h. */
-static bool
+bool
tree_expr_nonzero_p (tree t)
{
tree type = TREE_TYPE (t);
switch (TREE_CODE (t))
{
case ABS_EXPR:
- if (!TYPE_UNSIGNED (type) && !flag_wrapv)
- return tree_expr_nonzero_p (TREE_OPERAND (t, 0));
+ return tree_expr_nonzero_p (TREE_OPERAND (t, 0));
case INTEGER_CST:
/* We used to test for !integer_zerop here. This does not work correctly
return false;
/* Weak declarations may link to NULL. */
- if (DECL_P (base))
+ if (VAR_OR_FUNCTION_DECL_P (base))
return !DECL_WEAK (base);
/* Constants are never weak. */
return tree_expr_nonzero_p (TREE_OPERAND (t, 1))
|| tree_expr_nonzero_p (TREE_OPERAND (t, 0));
+ case CALL_EXPR:
+ return alloca_call_p (t);
+
default:
break;
}