@@ -1477,10 +1477,6 @@ Wtemplates
C++ ObjC++ Var(warn_templates) Warning
Warn on primary template declaration.
-Wtautological-compare
-C ObjC C++ ObjC++ Var(warn_tautological_compare) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
-Warn if a comparison always evaluates to true or false.
-
Wtemplate-body
C++ ObjC++ Var(warn_template_body) Warning Init(1)
Diagnose errors when parsing a template.
@@ -812,6 +812,10 @@ Wsystem-headers
Common Var(warn_system_headers) Warning
Do not suppress warnings from system headers.
+Wtautological-compare
+Common Var(warn_tautological_compare) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
+Warn if a comparison always evaluates to true or false.
+
Wtrampolines
Common Var(warn_trampolines) Warning
Warn whenever a trampoline is generated.
@@ -137,7 +137,6 @@ static tree range_successor (tree);
static tree fold_range_test (location_t, enum tree_code, tree, tree, tree);
static tree fold_cond_expr_with_comparison (location_t, tree, enum tree_code,
tree, tree, tree, tree);
-static tree unextend (tree, int, int, tree);
static tree extract_muldiv (tree, tree, enum tree_code, tree, bool *);
static tree extract_muldiv_1 (tree, tree, enum tree_code, tree, bool *);
static tree fold_binary_op_with_conditional_arg (location_t,
@@ -4711,7 +4710,7 @@ invert_truthvalue_loc (location_t loc, tree arg)
is the original memory reference used to preserve the alias set of
the access. */
-static tree
+tree
make_bit_field_ref (location_t loc, tree inner, tree orig_inner, tree type,
HOST_WIDE_INT bitsize, poly_int64 bitpos,
int unsignedp, int reversep)
@@ -4961,136 +4960,6 @@ optimize_bit_field_compare (location_t loc, enum tree_code code,
return lhs;
}
-/* Subroutine for fold_truth_andor_1: decode a field reference.
-
- If EXP is a comparison reference, we return the innermost reference.
-
- *PBITSIZE is set to the number of bits in the reference, *PBITPOS is
- set to the starting bit number.
-
- If the innermost field can be completely contained in a mode-sized
- unit, *PMODE is set to that mode. Otherwise, it is set to VOIDmode.
-
- *PVOLATILEP is set to 1 if the any expression encountered is volatile;
- otherwise it is not changed.
-
- *PUNSIGNEDP is set to the signedness of the field.
-
- *PREVERSEP is set to the storage order of the field.
-
- *PMASK is set to the mask used. This is either contained in a
- BIT_AND_EXPR or derived from the width of the field.
-
- *PAND_MASK is set to the mask found in a BIT_AND_EXPR, if any.
-
- Return 0 if this is not a component reference or is one that we can't
- do anything with. */
-
-static tree
-decode_field_reference (location_t loc, tree *exp_, HOST_WIDE_INT *pbitsize,
- HOST_WIDE_INT *pbitpos, machine_mode *pmode,
- int *punsignedp, int *preversep, int *pvolatilep,
- tree *pmask, tree *pand_mask)
-{
- tree exp = *exp_;
- tree outer_type = 0;
- tree and_mask = 0;
- tree mask, inner, offset;
- tree unsigned_type;
- unsigned int precision;
-
- /* All the optimizations using this function assume integer fields.
- There are problems with FP fields since the type_for_size call
- below can fail for, e.g., XFmode. */
- if (! INTEGRAL_TYPE_P (TREE_TYPE (exp)))
- return NULL_TREE;
-
- /* We are interested in the bare arrangement of bits, so strip everything
- that doesn't affect the machine mode. However, record the type of the
- outermost expression if it may matter below. */
- if (CONVERT_EXPR_P (exp)
- || TREE_CODE (exp) == NON_LVALUE_EXPR)
- outer_type = TREE_TYPE (exp);
- STRIP_NOPS (exp);
-
- if (TREE_CODE (exp) == BIT_AND_EXPR)
- {
- and_mask = TREE_OPERAND (exp, 1);
- exp = TREE_OPERAND (exp, 0);
- STRIP_NOPS (exp); STRIP_NOPS (and_mask);
- if (TREE_CODE (and_mask) != INTEGER_CST)
- return NULL_TREE;
- }
-
- poly_int64 poly_bitsize, poly_bitpos;
- inner = get_inner_reference (exp, &poly_bitsize, &poly_bitpos, &offset,
- pmode, punsignedp, preversep, pvolatilep);
- if ((inner == exp && and_mask == 0)
- || !poly_bitsize.is_constant (pbitsize)
- || !poly_bitpos.is_constant (pbitpos)
- || *pbitsize < 0
- || offset != 0
- || TREE_CODE (inner) == PLACEHOLDER_EXPR
- /* We eventually want to build a larger reference and need to take
- the address of this. */
- || (!REFERENCE_CLASS_P (inner) && !DECL_P (inner))
- /* Reject out-of-bound accesses (PR79731). */
- || (! AGGREGATE_TYPE_P (TREE_TYPE (inner))
- && compare_tree_int (TYPE_SIZE (TREE_TYPE (inner)),
- *pbitpos + *pbitsize) < 0))
- return NULL_TREE;
-
- unsigned_type = lang_hooks.types.type_for_size (*pbitsize, 1);
- if (unsigned_type == NULL_TREE)
- return NULL_TREE;
-
- *exp_ = exp;
-
- /* If the number of bits in the reference is the same as the bitsize of
- the outer type, then the outer type gives the signedness. Otherwise
- (in case of a small bitfield) the signedness is unchanged. */
- if (outer_type && *pbitsize == TYPE_PRECISION (outer_type))
- *punsignedp = TYPE_UNSIGNED (outer_type);
-
- /* Compute the mask to access the bitfield. */
- precision = TYPE_PRECISION (unsigned_type);
-
- mask = build_int_cst_type (unsigned_type, -1);
-
- mask = const_binop (LSHIFT_EXPR, mask, size_int (precision - *pbitsize));
- mask = const_binop (RSHIFT_EXPR, mask, size_int (precision - *pbitsize));
-
- /* Merge it with the mask we found in the BIT_AND_EXPR, if any. */
- if (and_mask != 0)
- mask = fold_build2_loc (loc, BIT_AND_EXPR, unsigned_type,
- fold_convert_loc (loc, unsigned_type, and_mask), mask);
-
- *pmask = mask;
- *pand_mask = and_mask;
- return inner;
-}
-
-/* Return nonzero if MASK represents a mask of SIZE ones in the low-order
- bit positions and MASK is SIGNED. */
-
-static bool
-all_ones_mask_p (const_tree mask, unsigned int size)
-{
- tree type = TREE_TYPE (mask);
- unsigned int precision = TYPE_PRECISION (type);
-
- /* If this function returns true when the type of the mask is
- UNSIGNED, then there will be errors. In particular see
- gcc.c-torture/execute/990326-1.c. There does not appear to be
- any documentation paper trail as to why this is so. But the pre
- wide-int worked with that restriction and it has been preserved
- here. */
- if (size > precision || TYPE_SIGN (type) == UNSIGNED)
- return false;
-
- return wi::mask (size, false, precision) == wi::to_wide (mask);
-}
-
/* Subroutine for fold: determine if VAL is the INTEGER_CONST that
represents the sign bit of EXP's type. If EXP represents a sign
or zero extension, also test VAL against the unextended type.
@@ -6400,48 +6269,6 @@ fold_range_test (location_t loc, enum tree_code code, tree type,
return 0;
}
-/* Subroutine for fold_truth_andor_1: C is an INTEGER_CST interpreted as a P
- bit value. Arrange things so the extra bits will be set to zero if and
- only if C is signed-extended to its full width. If MASK is nonzero,
- it is an INTEGER_CST that should be AND'ed with the extra bits. */
-
-static tree
-unextend (tree c, int p, int unsignedp, tree mask)
-{
- tree type = TREE_TYPE (c);
- int modesize = GET_MODE_BITSIZE (SCALAR_INT_TYPE_MODE (type));
- tree temp;
-
- if (p == modesize || unsignedp)
- return c;
-
- /* We work by getting just the sign bit into the low-order bit, then
- into the high-order bit, then sign-extend. We then XOR that value
- with C. */
- temp = build_int_cst (TREE_TYPE (c),
- wi::extract_uhwi (wi::to_wide (c), p - 1, 1));
-
- /* We must use a signed type in order to get an arithmetic right shift.
- However, we must also avoid introducing accidental overflows, so that
- a subsequent call to integer_zerop will work. Hence we must
- do the type conversion here. At this point, the constant is either
- zero or one, and the conversion to a signed type can never overflow.
- We could get an overflow if this conversion is done anywhere else. */
- if (TYPE_UNSIGNED (type))
- temp = fold_convert (signed_type_for (type), temp);
-
- temp = const_binop (LSHIFT_EXPR, temp, size_int (modesize - 1));
- temp = const_binop (RSHIFT_EXPR, temp, size_int (modesize - p - 1));
- if (mask != 0)
- temp = const_binop (BIT_AND_EXPR, temp,
- fold_convert (TREE_TYPE (c), mask));
- /* If necessary, convert the type back to match the type of C. */
- if (TYPE_UNSIGNED (type))
- temp = fold_convert (type, temp);
-
- return fold_convert (type, const_binop (BIT_XOR_EXPR, c, temp));
-}
-
/* For an expression that has the form
(A && B) || ~B
or
@@ -6512,20 +6339,13 @@ merge_truthop_with_opposite_arm (location_t loc, tree op, tree cmpop,
lhs, rhs);
return NULL_TREE;
}
-
+
/* Find ways of folding logical expressions of LHS and RHS:
Try to merge two comparisons to the same innermost item.
Look for range tests like "ch >= '0' && ch <= '9'".
Look for combinations of simple terms on machines with expensive branches
and evaluate the RHS unconditionally.
- For example, if we have p->a == 2 && p->b == 4 and we can make an
- object large enough to span both A and B, we can do this with a comparison
- against the object ANDed with the a mask.
-
- If we have p->a == q->a && p->b == q->b, we may be able to use bit masking
- operations to do this with one comparison.
-
We check for both normal comparisons and the BIT_AND_EXPRs made this by
function and the one above.
@@ -6550,24 +6370,9 @@ fold_truth_andor_1 (location_t loc, enum tree_code code, tree truth_type,
convert EQ_EXPR to NE_EXPR so we need not reject the "wrong"
comparison for one-bit fields. */
- enum tree_code wanted_code;
enum tree_code lcode, rcode;
tree ll_arg, lr_arg, rl_arg, rr_arg;
- tree ll_inner, lr_inner, rl_inner, rr_inner;
- HOST_WIDE_INT ll_bitsize, ll_bitpos, lr_bitsize, lr_bitpos;
- HOST_WIDE_INT rl_bitsize, rl_bitpos, rr_bitsize, rr_bitpos;
- HOST_WIDE_INT xll_bitpos, xlr_bitpos, xrl_bitpos, xrr_bitpos;
- HOST_WIDE_INT lnbitsize, lnbitpos, rnbitsize, rnbitpos;
- int ll_unsignedp, lr_unsignedp, rl_unsignedp, rr_unsignedp;
- int ll_reversep, lr_reversep, rl_reversep, rr_reversep;
- machine_mode ll_mode, lr_mode, rl_mode, rr_mode;
- scalar_int_mode lnmode, rnmode;
- tree ll_mask, lr_mask, rl_mask, rr_mask;
- tree ll_and_mask, lr_and_mask, rl_and_mask, rr_and_mask;
- tree l_const, r_const;
- tree lntype, rntype, result;
- HOST_WIDE_INT first_bit, end_bit;
- int volatilep;
+ tree result;
/* Start by getting the comparison codes. Fail if anything is volatile.
If one operand is a BIT_AND_EXPR with the constant one, treat it as if
@@ -6662,316 +6467,7 @@ fold_truth_andor_1 (location_t loc, enum tree_code code, tree truth_type,
build_int_cst (TREE_TYPE (ll_arg), 0));
}
- /* See if the comparisons can be merged. Then get all the parameters for
- each side. */
-
- if ((lcode != EQ_EXPR && lcode != NE_EXPR)
- || (rcode != EQ_EXPR && rcode != NE_EXPR))
- return 0;
-
- ll_reversep = lr_reversep = rl_reversep = rr_reversep = 0;
- volatilep = 0;
- ll_inner = decode_field_reference (loc, &ll_arg,
- &ll_bitsize, &ll_bitpos, &ll_mode,
- &ll_unsignedp, &ll_reversep, &volatilep,
- &ll_mask, &ll_and_mask);
- lr_inner = decode_field_reference (loc, &lr_arg,
- &lr_bitsize, &lr_bitpos, &lr_mode,
- &lr_unsignedp, &lr_reversep, &volatilep,
- &lr_mask, &lr_and_mask);
- rl_inner = decode_field_reference (loc, &rl_arg,
- &rl_bitsize, &rl_bitpos, &rl_mode,
- &rl_unsignedp, &rl_reversep, &volatilep,
- &rl_mask, &rl_and_mask);
- rr_inner = decode_field_reference (loc, &rr_arg,
- &rr_bitsize, &rr_bitpos, &rr_mode,
- &rr_unsignedp, &rr_reversep, &volatilep,
- &rr_mask, &rr_and_mask);
-
- /* It must be true that the inner operation on the lhs of each
- comparison must be the same if we are to be able to do anything.
- Then see if we have constants. If not, the same must be true for
- the rhs's. */
- if (volatilep
- || ll_reversep != rl_reversep
- || ll_inner == 0 || rl_inner == 0
- || ! operand_equal_p (ll_inner, rl_inner, 0))
- return 0;
-
- if (TREE_CODE (lr_arg) == INTEGER_CST
- && TREE_CODE (rr_arg) == INTEGER_CST)
- {
- l_const = lr_arg, r_const = rr_arg;
- lr_reversep = ll_reversep;
- }
- else if (lr_reversep != rr_reversep
- || lr_inner == 0 || rr_inner == 0
- || ! operand_equal_p (lr_inner, rr_inner, 0))
- return 0;
- else
- l_const = r_const = 0;
-
- /* If either comparison code is not correct for our logical operation,
- fail. However, we can convert a one-bit comparison against zero into
- the opposite comparison against that bit being set in the field. */
-
- wanted_code = (code == TRUTH_AND_EXPR ? EQ_EXPR : NE_EXPR);
- if (lcode != wanted_code)
- {
- if (l_const && integer_zerop (l_const) && integer_pow2p (ll_mask))
- {
- /* Make the left operand unsigned, since we are only interested
- in the value of one bit. Otherwise we are doing the wrong
- thing below. */
- ll_unsignedp = 1;
- l_const = ll_mask;
- }
- else
- return 0;
- }
-
- /* This is analogous to the code for l_const above. */
- if (rcode != wanted_code)
- {
- if (r_const && integer_zerop (r_const) && integer_pow2p (rl_mask))
- {
- rl_unsignedp = 1;
- r_const = rl_mask;
- }
- else
- return 0;
- }
-
- /* See if we can find a mode that contains both fields being compared on
- the left. If we can't, fail. Otherwise, update all constants and masks
- to be relative to a field of that size. */
- first_bit = MIN (ll_bitpos, rl_bitpos);
- end_bit = MAX (ll_bitpos + ll_bitsize, rl_bitpos + rl_bitsize);
- if (!get_best_mode (end_bit - first_bit, first_bit, 0, 0,
- TYPE_ALIGN (TREE_TYPE (ll_inner)), BITS_PER_WORD,
- volatilep, &lnmode))
- return 0;
-
- lnbitsize = GET_MODE_BITSIZE (lnmode);
- lnbitpos = first_bit & ~ (lnbitsize - 1);
- lntype = lang_hooks.types.type_for_size (lnbitsize, 1);
- xll_bitpos = ll_bitpos - lnbitpos, xrl_bitpos = rl_bitpos - lnbitpos;
-
- if (ll_reversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
- {
- xll_bitpos = lnbitsize - xll_bitpos - ll_bitsize;
- xrl_bitpos = lnbitsize - xrl_bitpos - rl_bitsize;
- }
-
- ll_mask = const_binop (LSHIFT_EXPR, fold_convert_loc (loc, lntype, ll_mask),
- size_int (xll_bitpos));
- rl_mask = const_binop (LSHIFT_EXPR, fold_convert_loc (loc, lntype, rl_mask),
- size_int (xrl_bitpos));
- if (ll_mask == NULL_TREE || rl_mask == NULL_TREE)
- return 0;
-
- if (l_const)
- {
- l_const = fold_convert_loc (loc, lntype, l_const);
- l_const = unextend (l_const, ll_bitsize, ll_unsignedp, ll_and_mask);
- l_const = const_binop (LSHIFT_EXPR, l_const, size_int (xll_bitpos));
- if (l_const == NULL_TREE)
- return 0;
- if (! integer_zerop (const_binop (BIT_AND_EXPR, l_const,
- fold_build1_loc (loc, BIT_NOT_EXPR,
- lntype, ll_mask))))
- {
- warning (0, "comparison is always %d", wanted_code == NE_EXPR);
-
- return constant_boolean_node (wanted_code == NE_EXPR, truth_type);
- }
- }
- if (r_const)
- {
- r_const = fold_convert_loc (loc, lntype, r_const);
- r_const = unextend (r_const, rl_bitsize, rl_unsignedp, rl_and_mask);
- r_const = const_binop (LSHIFT_EXPR, r_const, size_int (xrl_bitpos));
- if (r_const == NULL_TREE)
- return 0;
- if (! integer_zerop (const_binop (BIT_AND_EXPR, r_const,
- fold_build1_loc (loc, BIT_NOT_EXPR,
- lntype, rl_mask))))
- {
- warning (0, "comparison is always %d", wanted_code == NE_EXPR);
-
- return constant_boolean_node (wanted_code == NE_EXPR, truth_type);
- }
- }
-
- /* If the right sides are not constant, do the same for it. Also,
- disallow this optimization if a size, signedness or storage order
- mismatch occurs between the left and right sides. */
- if (l_const == 0)
- {
- if (ll_bitsize != lr_bitsize || rl_bitsize != rr_bitsize
- || ll_unsignedp != lr_unsignedp || rl_unsignedp != rr_unsignedp
- || ll_reversep != lr_reversep
- /* Make sure the two fields on the right
- correspond to the left without being swapped. */
- || ll_bitpos - rl_bitpos != lr_bitpos - rr_bitpos)
- return 0;
-
- first_bit = MIN (lr_bitpos, rr_bitpos);
- end_bit = MAX (lr_bitpos + lr_bitsize, rr_bitpos + rr_bitsize);
- if (!get_best_mode (end_bit - first_bit, first_bit, 0, 0,
- TYPE_ALIGN (TREE_TYPE (lr_inner)), BITS_PER_WORD,
- volatilep, &rnmode))
- return 0;
-
- rnbitsize = GET_MODE_BITSIZE (rnmode);
- rnbitpos = first_bit & ~ (rnbitsize - 1);
- rntype = lang_hooks.types.type_for_size (rnbitsize, 1);
- xlr_bitpos = lr_bitpos - rnbitpos, xrr_bitpos = rr_bitpos - rnbitpos;
-
- if (lr_reversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
- {
- xlr_bitpos = rnbitsize - xlr_bitpos - lr_bitsize;
- xrr_bitpos = rnbitsize - xrr_bitpos - rr_bitsize;
- }
-
- lr_mask = const_binop (LSHIFT_EXPR, fold_convert_loc (loc,
- rntype, lr_mask),
- size_int (xlr_bitpos));
- rr_mask = const_binop (LSHIFT_EXPR, fold_convert_loc (loc,
- rntype, rr_mask),
- size_int (xrr_bitpos));
- if (lr_mask == NULL_TREE || rr_mask == NULL_TREE)
- return 0;
-
- /* Make a mask that corresponds to both fields being compared.
- Do this for both items being compared. If the operands are the
- same size and the bits being compared are in the same position
- then we can do this by masking both and comparing the masked
- results. */
- ll_mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask);
- lr_mask = const_binop (BIT_IOR_EXPR, lr_mask, rr_mask);
- if (lnbitsize == rnbitsize
- && xll_bitpos == xlr_bitpos
- && lnbitpos >= 0
- && rnbitpos >= 0)
- {
- lhs = make_bit_field_ref (loc, ll_inner, ll_arg,
- lntype, lnbitsize, lnbitpos,
- ll_unsignedp || rl_unsignedp, ll_reversep);
- if (! all_ones_mask_p (ll_mask, lnbitsize))
- lhs = build2 (BIT_AND_EXPR, lntype, lhs, ll_mask);
-
- rhs = make_bit_field_ref (loc, lr_inner, lr_arg,
- rntype, rnbitsize, rnbitpos,
- lr_unsignedp || rr_unsignedp, lr_reversep);
- if (! all_ones_mask_p (lr_mask, rnbitsize))
- rhs = build2 (BIT_AND_EXPR, rntype, rhs, lr_mask);
-
- return build2_loc (loc, wanted_code, truth_type, lhs, rhs);
- }
-
- /* There is still another way we can do something: If both pairs of
- fields being compared are adjacent, we may be able to make a wider
- field containing them both.
-
- Note that we still must mask the lhs/rhs expressions. Furthermore,
- the mask must be shifted to account for the shift done by
- make_bit_field_ref. */
- if (((ll_bitsize + ll_bitpos == rl_bitpos
- && lr_bitsize + lr_bitpos == rr_bitpos)
- || (ll_bitpos == rl_bitpos + rl_bitsize
- && lr_bitpos == rr_bitpos + rr_bitsize))
- && ll_bitpos >= 0
- && rl_bitpos >= 0
- && lr_bitpos >= 0
- && rr_bitpos >= 0)
- {
- tree type;
-
- lhs = make_bit_field_ref (loc, ll_inner, ll_arg, lntype,
- ll_bitsize + rl_bitsize,
- MIN (ll_bitpos, rl_bitpos),
- ll_unsignedp, ll_reversep);
- rhs = make_bit_field_ref (loc, lr_inner, lr_arg, rntype,
- lr_bitsize + rr_bitsize,
- MIN (lr_bitpos, rr_bitpos),
- lr_unsignedp, lr_reversep);
-
- ll_mask = const_binop (RSHIFT_EXPR, ll_mask,
- size_int (MIN (xll_bitpos, xrl_bitpos)));
- lr_mask = const_binop (RSHIFT_EXPR, lr_mask,
- size_int (MIN (xlr_bitpos, xrr_bitpos)));
- if (ll_mask == NULL_TREE || lr_mask == NULL_TREE)
- return 0;
-
- /* Convert to the smaller type before masking out unwanted bits. */
- type = lntype;
- if (lntype != rntype)
- {
- if (lnbitsize > rnbitsize)
- {
- lhs = fold_convert_loc (loc, rntype, lhs);
- ll_mask = fold_convert_loc (loc, rntype, ll_mask);
- type = rntype;
- }
- else if (lnbitsize < rnbitsize)
- {
- rhs = fold_convert_loc (loc, lntype, rhs);
- lr_mask = fold_convert_loc (loc, lntype, lr_mask);
- type = lntype;
- }
- }
-
- if (! all_ones_mask_p (ll_mask, ll_bitsize + rl_bitsize))
- lhs = build2 (BIT_AND_EXPR, type, lhs, ll_mask);
-
- if (! all_ones_mask_p (lr_mask, lr_bitsize + rr_bitsize))
- rhs = build2 (BIT_AND_EXPR, type, rhs, lr_mask);
-
- return build2_loc (loc, wanted_code, truth_type, lhs, rhs);
- }
-
- return 0;
- }
-
- /* Handle the case of comparisons with constants. If there is something in
- common between the masks, those bits of the constants must be the same.
- If not, the condition is always false. Test for this to avoid generating
- incorrect code below. */
- result = const_binop (BIT_AND_EXPR, ll_mask, rl_mask);
- if (! integer_zerop (result)
- && simple_cst_equal (const_binop (BIT_AND_EXPR, result, l_const),
- const_binop (BIT_AND_EXPR, result, r_const)) != 1)
- {
- if (wanted_code == NE_EXPR)
- {
- warning (0, "%<or%> of unmatched not-equal tests is always 1");
- return constant_boolean_node (true, truth_type);
- }
- else
- {
- warning (0, "%<and%> of mutually exclusive equal-tests is always 0");
- return constant_boolean_node (false, truth_type);
- }
- }
-
- if (lnbitpos < 0)
- return 0;
-
- /* Construct the expression we will return. First get the component
- reference we will make. Unless the mask is all ones the width of
- that field, perform the mask operation. Then compare with the
- merged constant. */
- result = make_bit_field_ref (loc, ll_inner, ll_arg,
- lntype, lnbitsize, lnbitpos,
- ll_unsignedp || rl_unsignedp, ll_reversep);
-
- ll_mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask);
- if (! all_ones_mask_p (ll_mask, lnbitsize))
- result = build2_loc (loc, BIT_AND_EXPR, lntype, result, ll_mask);
-
- return build2_loc (loc, wanted_code, truth_type, result,
- const_binop (BIT_IOR_EXPR, l_const, r_const));
+ return 0;
}
/* T is an integer expression that is being multiplied, divided, or taken a
@@ -253,11 +253,21 @@ extern tree fold_build_pointer_plus_hwi_loc (location_t loc, tree ptr, HOST_WIDE
extern tree_code minmax_from_comparison (tree_code, tree, tree,
tree, tree);
+extern tree make_bit_field_ref (location_t, tree, tree, tree,
+ HOST_WIDE_INT, poly_int64, int, int);
+
/* In gimple-fold.cc. */
extern void clear_type_padding_in_mask (tree, unsigned char *);
extern bool clear_padding_type_may_have_padding_p (tree);
extern bool arith_overflowed_p (enum tree_code, const_tree, const_tree,
const_tree);
+extern tree fold_truth_andor_for_ifcombine (enum tree_code, tree,
+ location_t, enum tree_code,
+ tree, tree,
+ location_t, enum tree_code,
+ tree, tree,
+ tree *);
+
/* Class used to compare gimple operands. */
@@ -7438,7 +7438,1159 @@ maybe_fold_comparisons_from_match_pd (tree type, enum tree_code code,
return NULL_TREE;
}
+
+/* Return TRUE and set op[0] if T, following all SSA edges, is a type
+ conversion. */
+static bool
+gimple_fold_follow_convert (tree t, tree op[1])
+{
+ if (TREE_CODE (t) == SSA_NAME
+ && !SSA_NAME_IS_DEFAULT_DEF (t))
+ if (gassign *def = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (t)))
+ switch (gimple_assign_rhs_code (def))
+ {
+ CASE_CONVERT:
+ op[0] = gimple_assign_rhs1 (def);
+ return true;
+ case VIEW_CONVERT_EXPR:
+ op[0] = TREE_OPERAND (gimple_assign_rhs1 (def), 0);
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+/* Return TRUE and set op[*] if T, following all SSA edges, resolves to a
+ binary expression with code CODE. */
+
+static bool
+gimple_fold_binop_cst (enum tree_code code, tree t, tree op[2])
+{
+ if (TREE_CODE (t) == SSA_NAME
+ && !SSA_NAME_IS_DEFAULT_DEF (t))
+ if (gimple *def = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (t)))
+ if (gimple_assign_rhs_code (def) == code)
+ {
+ tree op0 = gimple_assign_rhs1 (def);
+ tree op1 = gimple_assign_rhs2 (def);
+ if (tree_swap_operands_p (op0, op1))
+ std::swap (op0, op1);
+ if (uniform_integer_cst_p (op1))
+ {
+ op[0] = op0;
+ op[1] = op1;
+ return true;
+ }
+ }
+ return false;
+}
+/* Subroutine for fold_truth_andor_1: decode a field reference.
+
+ If *PEXP is a comparison reference, we return the innermost reference.
+
+ *PBITSIZE is set to the number of bits in the reference, *PBITPOS is
+ set to the starting bit number.
+
+ *PVOLATILEP is set to 1 if the any expression encountered is volatile;
+ otherwise it is not changed.
+
+ *PUNSIGNEDP is set to the signedness of the field.
+
+ *PREVERSEP is set to the storage order of the field.
+
+ *PAND_MASK is set to the mask found in a BIT_AND_EXPR, if any.
+ If PAND_MASK *is NULL, BIT_AND_EXPR is not recognized.
+
+ *XOR_P is to be FALSE if EXP might be a XOR used in a compare, in which
+ case, if XOR_CMP_OP is a zero constant, it will be overridden with *PEXP,
+ *XOR_P will be set to TRUE, and the left-hand operand of the XOR will be
+ decoded. If *XOR_P is TRUE, XOR_CMP_OP is supposed to be NULL, and then the
+ right-hand operand of the XOR will be decoded.
+
+ *LOAD is set to the load stmt of the innermost reference, if any,
+ *and NULL otherwise.
+
+ LOC[0..3] are filled in as conversion, masking, shifting and loading
+ operations are located.
+
+ Return 0 if this is not a component reference or is one that we can't
+ do anything with. */
+
+static tree
+decode_field_reference (tree *pexp, HOST_WIDE_INT *pbitsize,
+ HOST_WIDE_INT *pbitpos,
+ bool *punsignedp, bool *preversep, bool *pvolatilep,
+ wide_int *pand_mask, bool *xor_p, tree *xor_cmp_op,
+ gimple **load, location_t loc[4])
+{
+ tree exp = *pexp;
+ tree outer_type = 0;
+ wide_int and_mask;
+ tree inner, offset;
+ int shiftrt = 0;
+ tree res_ops[2];
+ machine_mode mode;
+
+ *load = NULL;
+
+ /* All the optimizations using this function assume integer fields.
+ There are problems with FP fields since the type_for_size call
+ below can fail for, e.g., XFmode. */
+ if (! INTEGRAL_TYPE_P (TREE_TYPE (exp)))
+ return NULL_TREE;
+
+ /* Drop casts, only save the outermost type. We need not worry about
+ narrowing then widening casts, or vice-versa, for those that are not
+ essential for the compare have already been optimized out at this
+ point. */
+ if (gimple_fold_follow_convert (exp, res_ops))
+ {
+ if (!outer_type)
+ {
+ outer_type = TREE_TYPE (exp);
+ loc[0] = gimple_location (SSA_NAME_DEF_STMT (exp));
+ }
+ exp = res_ops[0];
+ }
+
+ /* Recognize and save a masking operation. */
+ if (pand_mask && gimple_fold_binop_cst (BIT_AND_EXPR, exp, res_ops))
+ {
+ loc[1] = gimple_location (SSA_NAME_DEF_STMT (exp));
+ exp = res_ops[0];
+ and_mask = wi::to_wide (res_ops[1]);
+ }
+
+ /* Turn (a ^ b) [!]= 0 into a [!]= b. */
+ if (xor_p && gimple_fold_binop_cst (BIT_XOR_EXPR, exp, res_ops))
+ {
+ /* No location recorded for this one, it's entirely subsumed by the
+ compare. */
+ if (*xor_p)
+ {
+ exp = res_ops[1];
+ gcc_checking_assert (!xor_cmp_op);
+ }
+ else if (!xor_cmp_op)
+ /* Not much we can do when xor appears in the right-hand compare
+ operand. */
+ return NULL_TREE;
+ else
+ {
+ *xor_p = true;
+ exp = res_ops[0];
+ *xor_cmp_op = *pexp;
+ }
+ }
+
+ /* Another chance to drop conversions. */
+ if (gimple_fold_follow_convert (exp, res_ops))
+ {
+ if (!outer_type)
+ {
+ outer_type = TREE_TYPE (exp);
+ loc[0] = gimple_location (SSA_NAME_DEF_STMT (exp));
+ }
+ exp = res_ops[0];
+ }
+
+ /* Take note of shifts. */
+ if (gimple_fold_binop_cst (RSHIFT_EXPR, exp, res_ops))
+ {
+ loc[2] = gimple_location (SSA_NAME_DEF_STMT (exp));
+ exp = res_ops[0];
+ if (!tree_fits_shwi_p (res_ops[1]))
+ return NULL_TREE;
+ shiftrt = tree_to_shwi (res_ops[1]);
+ if (shiftrt <= 0)
+ return NULL_TREE;
+ }
+
+ /* Yet another chance to drop conversions. */
+ if (gimple_fold_follow_convert (exp, res_ops))
+ {
+ if (!outer_type)
+ {
+ outer_type = TREE_TYPE (exp);
+ loc[0] = gimple_location (SSA_NAME_DEF_STMT (exp));
+ }
+ exp = res_ops[0];
+ }
+
+ /* Identify the load, if there is one. */
+ if (TREE_CODE (exp) == SSA_NAME
+ && !SSA_NAME_IS_DEFAULT_DEF (exp))
+ {
+ gimple *def = SSA_NAME_DEF_STMT (exp);
+ if (gimple_assign_load_p (def))
+ {
+ loc[3] = gimple_location (def);
+ *load = def;
+ exp = gimple_assign_rhs1 (def);
+ }
+ }
+
+ /* Identify the relevant bits. */
+ poly_int64 poly_bitsize, poly_bitpos;
+ int unsignedp, reversep = *preversep, volatilep = *pvolatilep;
+ inner = get_inner_reference (exp, &poly_bitsize, &poly_bitpos, &offset,
+ &mode, &unsignedp, &reversep, &volatilep);
+
+ HOST_WIDE_INT bs, bp;
+ if (!poly_bitsize.is_constant (&bs)
+ || !poly_bitpos.is_constant (&bp)
+ || bs <= shiftrt
+ || offset != 0
+ || TREE_CODE (inner) == PLACEHOLDER_EXPR
+ /* Reject out-of-bound accesses (PR79731). */
+ || (! AGGREGATE_TYPE_P (TREE_TYPE (inner))
+ && compare_tree_int (TYPE_SIZE (TREE_TYPE (inner)),
+ bp + bs) < 0))
+ return NULL_TREE;
+
+ *pbitsize = bs;
+ *pbitpos = bp;
+ *punsignedp = unsignedp;
+ *preversep = reversep;
+ *pvolatilep = volatilep;
+
+ /* Adjust shifts... */
+ if (shiftrt)
+ {
+ if (!*preversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+ *pbitpos += shiftrt;
+ *pbitsize -= shiftrt;
+ }
+
+ /* ... and bit position. */
+ if (outer_type && *pbitsize > TYPE_PRECISION (outer_type))
+ {
+ HOST_WIDE_INT excess = *pbitsize - TYPE_PRECISION (outer_type);
+ if (*preversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+ *pbitpos += excess;
+ *pbitsize -= excess;
+ }
+
+ *pexp = exp;
+
+ /* If the number of bits in the reference is the same as the bitsize of
+ the outer type, then the outer type gives the signedness. Otherwise
+ (in case of a small bitfield) the signedness is unchanged. */
+ if (outer_type && *pbitsize == TYPE_PRECISION (outer_type))
+ *punsignedp = TYPE_UNSIGNED (outer_type);
+
+ /* Make the mask the expected width. */
+ if (and_mask.get_precision () != 0)
+ and_mask = wide_int::from (and_mask, *pbitsize, UNSIGNED);
+
+ if (pand_mask)
+ *pand_mask = and_mask;
+
+ return inner;
+}
+
+/* Return the one bitpos within bit extents L or R that is at an
+ ALIGN-bit alignment boundary, or -1 if there is more than one such
+ boundary, if there isn't any, or if there is any such boundary
+ between the extents. L and R are given by bitpos and bitsize. If
+ it doesn't return -1, there are two consecutive ALIGN-bit words
+ that contain both extents, and at least one of the extents
+ straddles across the returned alignment boundary. */
+
+static inline HOST_WIDE_INT
+compute_split_boundary_from_align (HOST_WIDE_INT align,
+ HOST_WIDE_INT l_bitpos,
+ HOST_WIDE_INT l_bitsize,
+ HOST_WIDE_INT r_bitpos,
+ HOST_WIDE_INT r_bitsize)
+{
+ HOST_WIDE_INT amask = ~(align - 1);
+
+ HOST_WIDE_INT first_bit = MIN (l_bitpos, r_bitpos);
+ HOST_WIDE_INT end_bit = MAX (l_bitpos + l_bitsize, r_bitpos + r_bitsize);
+
+ HOST_WIDE_INT boundary = (end_bit - 1) & amask;
+
+ /* Make sure we're crossing no more than one alignment boundary.
+
+ ??? We don't have logic to recombine loads of two adjacent
+ fields that each crosses a different alignment boundary, so
+ as to load the middle word only once, if other words can't be
+ otherwise recombined. */
+ if (boundary - first_bit > align)
+ return -1;
+
+ HOST_WIDE_INT l_start_word = l_bitpos & amask;
+ HOST_WIDE_INT l_end_word = (l_bitpos + l_bitsize - 1) & amask;
+
+ HOST_WIDE_INT r_start_word = r_bitpos & amask;
+ HOST_WIDE_INT r_end_word = (r_bitpos + r_bitsize - 1) & amask;
+
+ /* If neither field straddles across an alignment boundary, it's no
+ use to even try to merge them. */
+ if (l_start_word == l_end_word && r_start_word == r_end_word)
+ return -1;
+
+ return boundary;
+}
+
+/* Make a bit_field_ref. If POINT is NULL, return the BIT_FIELD_REF.
+ Otherwise, build and insert a load stmt before POINT, and return
+ the SSA_NAME. ??? Rewrite LOAD in terms of the bitfield? */
+
+static tree
+make_bit_field_load (location_t loc, tree inner, tree orig_inner, tree type,
+ HOST_WIDE_INT bitsize, poly_int64 bitpos,
+ bool unsignedp, bool reversep, gimple *point)
+{
+ if (point && loc == UNKNOWN_LOCATION)
+ loc = gimple_location (point);
+
+ tree ref = make_bit_field_ref (loc, unshare_expr (inner),
+ unshare_expr (orig_inner),
+ type, bitsize, bitpos,
+ unsignedp, reversep);
+ if (!point)
+ return ref;
+
+ gimple_seq stmts = NULL;
+ tree ret = force_gimple_operand (ref, &stmts, true, NULL_TREE);
+
+ /* We know the vuse is supposed to end up being the same as that at the
+ original load at the insertion point, but if we don't set it, it will be a
+ generic placeholder that only the global SSA update at the end of the pass
+ would make equal, too late for us to use in further combinations. So go
+ ahead and copy the vuse. */
+
+ tree reaching_vuse = gimple_vuse (point);
+ for (gimple_stmt_iterator i = gsi_start (stmts);
+ !gsi_end_p (i); gsi_next (&i))
+ {
+ gimple *new_stmt = gsi_stmt (i);
+ if (gimple_has_mem_ops (new_stmt))
+ gimple_set_vuse (new_stmt, reaching_vuse);
+ }
+
+ gimple_stmt_iterator gsi = gsi_for_stmt (point);
+ gsi_insert_seq_before (&gsi, stmts, GSI_SAME_STMT);
+ return ret;
+}
+
+/* Initialize ln_arg[0] and ln_arg[1] to a pair of newly-created (at
+ LOC) loads from INNER (from ORIG_INNER), of modes MODE and MODE2,
+ respectively, starting at BIT_POS, using reversed endianness if
+ REVERSEP. Also initialize BITPOS (the starting position of each
+ part into INNER), BITSIZ (the bit count starting at BITPOS),
+ TOSHIFT[1] (the amount by which the part and its mask are to be
+ shifted right to bring its least-significant bit to bit zero) and
+ SHIFTED (the amount by which the part, by separate loading, has
+ already been shifted right, but that the mask needs shifting to
+ match). */
+
+static inline void
+build_split_load (tree /* out */ ln_arg[2],
+ HOST_WIDE_INT /* out */ bitpos[2],
+ HOST_WIDE_INT /* out */ bitsiz[2],
+ HOST_WIDE_INT /* in[0] out[0..1] */ toshift[2],
+ HOST_WIDE_INT /* out */ shifted[2],
+ location_t loc, tree inner, tree orig_inner,
+ scalar_int_mode mode, scalar_int_mode mode2,
+ HOST_WIDE_INT bit_pos, bool reversep,
+ gimple *point[2])
+{
+ scalar_int_mode modes[2] = { mode, mode2 };
+ bitsiz[0] = GET_MODE_BITSIZE (mode);
+ bitsiz[1] = GET_MODE_BITSIZE (mode2);
+
+ for (int i = 0; i < 2; i++)
+ {
+ tree type = lang_hooks.types.type_for_mode (modes[i], 1);
+ if (!type)
+ {
+ type = build_nonstandard_integer_type (bitsiz[0], 1);
+ gcc_assert (type);
+ }
+ bitpos[i] = bit_pos;
+ ln_arg[i] = make_bit_field_load (loc, inner, orig_inner,
+ type, bitsiz[i],
+ bit_pos, 1, reversep, point[i]);
+ bit_pos += bitsiz[i];
+ }
+
+ toshift[1] = toshift[0];
+ if (reversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+ {
+ shifted[0] = bitsiz[1];
+ shifted[1] = 0;
+ toshift[0] = 0;
+ }
+ else
+ {
+ shifted[1] = bitsiz[0];
+ shifted[0] = 0;
+ toshift[1] = 0;
+ }
+}
+
+/* Make arrangements to split at bit BOUNDARY a single loaded word
+ (with REVERSEP bit order) LN_ARG[0], to be shifted right by
+ TOSHIFT[0] to bring the field of interest to the least-significant
+ bit. The expectation is that the same loaded word will be
+ propagated from part 0 to part 1, with just different shifting and
+ masking to extract both parts. MASK is not expected to do more
+ than masking out the bits that belong to the other part. See
+ build_split_load for more information on the other fields. */
+
+static inline void
+reuse_split_load (tree /* in[0] out[1] */ ln_arg[2],
+ HOST_WIDE_INT /* in[0] out[1] */ bitpos[2],
+ HOST_WIDE_INT /* in[0] out[1] */ bitsiz[2],
+ HOST_WIDE_INT /* in[0] out[0..1] */ toshift[2],
+ HOST_WIDE_INT /* out */ shifted[2],
+ wide_int /* out */ mask[2],
+ HOST_WIDE_INT boundary, bool reversep)
+{
+ unsigned prec = TYPE_PRECISION (TREE_TYPE (ln_arg[0]));
+
+ ln_arg[1] = ln_arg[0];
+ bitpos[1] = bitpos[0];
+ bitsiz[1] = bitsiz[0];
+ shifted[1] = shifted[0] = 0;
+
+ if (reversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+ {
+ toshift[1] = toshift[0];
+ toshift[0] = bitpos[0] + bitsiz[0] - boundary;
+ mask[0] = wi::mask (toshift[0], true, prec);
+ mask[1] = wi::mask (toshift[0], false, prec);
+ }
+ else
+ {
+ toshift[1] = boundary - bitpos[1];
+ mask[1] = wi::mask (toshift[1], true, prec);
+ mask[0] = wi::mask (toshift[1], false, prec);
+ }
+}
+
+/* Find ways of folding logical expressions of LHS and RHS:
+
+ Try to merge two comparisons to nearby fields.
+
+ For example, if we have p->a == 2 && p->b == 4 and we can load both A and B
+ at once, we can do this with a comparison against the object ANDed with the
+ a mask.
+
+ If we have p->a == q->a && p->b == q->b, we may be able to use bit masking
+ operations to do this with one comparison, loading both fields from P at
+ once, and likewise from Q.
+
+ Herein, loading at once means loading from within the same alignment
+ boundary for the enclosing object. If (packed) fields cross such alignment
+ boundaries, we may still recombine the compares, so that loads do not cross
+ the boundaries.
+
+ CODE is the logical operation being done. It can be TRUTH_ANDIF_EXPR,
+ TRUTH_AND_EXPR, TRUTH_ORIF_EXPR, or TRUTH_OR_EXPR.
+
+ TRUTH_TYPE is the type of the logical operand.
+
+ LHS is denoted as LL_ARG LCODE LR_ARG.
+
+ RHS is denoted as RL_ARG RCODE RR_ARG.
+
+ LHS is assumed to dominate RHS.
+
+ Combined loads are inserted next to preexisting loads, once we determine
+ that the combination is viable, and the combined condition references new
+ SSA_NAMEs that hold the loaded values. Since the original loads are
+ verified to have the same gimple_vuse, the insertion point doesn't matter
+ for correctness. ??? The loads may be a lot earlier than the compares, and
+ it's conceivable that one or two loads for RHS appear before those for LHS.
+ It could be advantageous to try to place the loads optimally, taking
+ advantage of knowing whether RHS is accessed before LHS, or that both are
+ accessed before both compares, but we don't do that (yet?).
+
+ SEPARATEP should be NULL if the combined condition must be returned as a
+ single expression, even if it is a compound condition. This must only be
+ done if LHS and RHS are adjacent, without intervening conditions, and the
+ combined condition is to replace RHS, while LHS is dropped altogether.
+
+ Otherwise, SEPARATEP must be a non-NULL pointer to a NULL_TREE, that may be
+ replaced by a part of the compound condition that could replace RHS, while
+ the returned expression replaces LHS. This works whether or not LHS and RHS
+ are adjacent, as long as there aren't VDEFs or other side effects between
+ them.
+
+ If the "words" accessed by RHS are already accessed by LHS, this won't
+ matter, but if RHS accesses "words" that LHS doesn't, then *SEPARATEP will
+ be set to the compares that should take RHS's place. By "words" we mean
+ contiguous bits that do not cross a an TYPE_ALIGN boundary of the accessed
+ object's type.
+
+ We return the simplified tree or 0 if no optimization is possible. */
+
+tree
+fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type,
+ location_t lloc, enum tree_code lcode,
+ tree ll_arg, tree lr_arg,
+ location_t rloc, enum tree_code rcode,
+ tree rl_arg, tree rr_arg,
+ tree *separatep)
+{
+ /* If this is the "or" of two comparisons, we can do something if
+ the comparisons are NE_EXPR. If this is the "and", we can do something
+ if the comparisons are EQ_EXPR. I.e.,
+ (a->b == 2 && a->c == 4) can become (a->new == NEW).
+
+ WANTED_CODE is this operation code. For single bit fields, we can
+ convert EQ_EXPR to NE_EXPR so we need not reject the "wrong"
+ comparison for one-bit fields. */
+
+ enum tree_code orig_code = code;
+ enum tree_code wanted_code;
+ tree ll_inner, lr_inner, rl_inner, rr_inner;
+ gimple *ll_load, *lr_load, *rl_load, *rr_load;
+ HOST_WIDE_INT ll_bitsize, ll_bitpos, lr_bitsize, lr_bitpos;
+ HOST_WIDE_INT rl_bitsize, rl_bitpos, rr_bitsize, rr_bitpos;
+ HOST_WIDE_INT xll_bitpos, xlr_bitpos, xrl_bitpos, xrr_bitpos;
+ HOST_WIDE_INT lnbitsize, lnbitpos, lnprec;
+ HOST_WIDE_INT rnbitsize, rnbitpos, rnprec;
+ bool ll_unsignedp, lr_unsignedp, rl_unsignedp, rr_unsignedp;
+ bool ll_reversep, lr_reversep, rl_reversep, rr_reversep;
+ scalar_int_mode lnmode, lnmode2, rnmode;
+ wide_int ll_and_mask, lr_and_mask, rl_and_mask, rr_and_mask;
+ wide_int l_const, r_const;
+ tree lntype, rntype, result;
+ HOST_WIDE_INT first_bit, end_bit;
+ bool volatilep;
+ bool l_split_load;
+
+ /* These are indexed by: conv, mask, shft, load. */
+ location_t ll_loc[4] = { lloc, lloc, lloc, UNKNOWN_LOCATION };
+ location_t lr_loc[4] = { lloc, lloc, lloc, UNKNOWN_LOCATION };
+ location_t rl_loc[4] = { rloc, rloc, rloc, UNKNOWN_LOCATION };
+ location_t rr_loc[4] = { rloc, rloc, rloc, UNKNOWN_LOCATION };
+
+ gcc_checking_assert (!separatep || !*separatep);
+
+ /* Start by getting the comparison codes. Fail if anything is volatile.
+ If one operand is a BIT_AND_EXPR with the constant one, treat it as if
+ it were surrounded with a NE_EXPR. */
+
+ if (TREE_CODE_CLASS (lcode) != tcc_comparison
+ || TREE_CODE_CLASS (rcode) != tcc_comparison)
+ return 0;
+
+ /* We don't normally find TRUTH_*IF_EXPR in gimple, but these codes may be
+ given by our caller to denote conditions from different blocks. */
+ switch (code)
+ {
+ case TRUTH_AND_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ code = TRUTH_AND_EXPR;
+ break;
+
+ case TRUTH_OR_EXPR:
+ case TRUTH_ORIF_EXPR:
+ code = TRUTH_OR_EXPR;
+ break;
+
+ default:
+ return 0;
+ }
+
+ bool lsignbit = false, rsignbit = false;
+ if ((lcode == LT_EXPR || lcode == GE_EXPR)
+ && integer_zerop (lr_arg)
+ && INTEGRAL_TYPE_P (TREE_TYPE (ll_arg))
+ && !TYPE_UNSIGNED (TREE_TYPE (ll_arg)))
+ {
+ lsignbit = true;
+ lcode = (lcode == LT_EXPR ? NE_EXPR : EQ_EXPR);
+ }
+ if ((rcode == LT_EXPR || rcode == GE_EXPR)
+ && integer_zerop (rr_arg)
+ && INTEGRAL_TYPE_P (TREE_TYPE (rl_arg))
+ && !TYPE_UNSIGNED (TREE_TYPE (rl_arg)))
+ {
+ rsignbit = true;
+ rcode = (rcode == LT_EXPR ? NE_EXPR : EQ_EXPR);
+ }
+
+ /* See if the comparisons can be merged. Then get all the parameters for
+ each side. */
+
+ if ((lcode != EQ_EXPR && lcode != NE_EXPR)
+ || (rcode != EQ_EXPR && rcode != NE_EXPR))
+ return 0;
+
+ ll_reversep = lr_reversep = rl_reversep = rr_reversep = 0;
+ volatilep = 0;
+ bool l_xor = false, r_xor = false;
+ ll_inner = decode_field_reference (&ll_arg, &ll_bitsize, &ll_bitpos,
+ &ll_unsignedp, &ll_reversep, &volatilep,
+ &ll_and_mask, &l_xor, &lr_arg,
+ &ll_load, ll_loc);
+ lr_inner = decode_field_reference (&lr_arg, &lr_bitsize, &lr_bitpos,
+ &lr_unsignedp, &lr_reversep, &volatilep,
+ &lr_and_mask, &l_xor, 0,
+ &lr_load, lr_loc);
+ rl_inner = decode_field_reference (&rl_arg, &rl_bitsize, &rl_bitpos,
+ &rl_unsignedp, &rl_reversep, &volatilep,
+ &rl_and_mask, &r_xor, &rr_arg,
+ &rl_load, rl_loc);
+ rr_inner = decode_field_reference (&rr_arg, &rr_bitsize, &rr_bitpos,
+ &rr_unsignedp, &rr_reversep, &volatilep,
+ &rr_and_mask, &r_xor, 0,
+ &rr_load, rr_loc);
+
+ /* It must be true that the inner operation on the lhs of each
+ comparison must be the same if we are to be able to do anything.
+ Then see if we have constants. If not, the same must be true for
+ the rhs's. */
+ if (volatilep
+ || ll_reversep != rl_reversep
+ || ll_inner == 0 || rl_inner == 0
+ || ! operand_equal_p (ll_inner, rl_inner, 0)
+ || (ll_load && rl_load
+ && gimple_vuse (ll_load) != gimple_vuse (rl_load)))
+ return 0;
+
+ if (TREE_CODE (lr_arg) == INTEGER_CST
+ && TREE_CODE (rr_arg) == INTEGER_CST)
+ {
+ l_const = wi::to_wide (lr_arg);
+ /* We don't expect masks on constants, but if there are any, apply
+ them now. */
+ if (lr_and_mask.get_precision ())
+ l_const &= wide_int::from (lr_and_mask,
+ l_const.get_precision (), UNSIGNED);
+ r_const = wi::to_wide (rr_arg);
+ if (rr_and_mask.get_precision ())
+ r_const &= wide_int::from (rr_and_mask,
+ r_const.get_precision (), UNSIGNED);
+ lr_reversep = ll_reversep;
+ }
+ else if (lr_reversep != rr_reversep
+ || lr_inner == 0 || rr_inner == 0
+ || ! operand_equal_p (lr_inner, rr_inner, 0)
+ || (lr_load && rr_load
+ && gimple_vuse (lr_load) != gimple_vuse (rr_load)))
+ return 0;
+
+ /* If we found sign tests, finish turning them into bit tests. */
+
+ if (lsignbit)
+ {
+ wide_int sign = wi::mask (ll_bitsize - 1, true, ll_bitsize);
+ if (!ll_and_mask.get_precision ())
+ ll_and_mask = sign;
+ else
+ ll_and_mask &= sign;
+ }
+
+ if (rsignbit)
+ {
+ wide_int sign = wi::mask (rl_bitsize - 1, true, rl_bitsize);
+ if (!rl_and_mask.get_precision ())
+ rl_and_mask = sign;
+ else
+ rl_and_mask &= sign;
+ }
+
+ /* If either comparison code is not correct for our logical operation,
+ fail. However, we can convert a one-bit comparison against zero into
+ the opposite comparison against that bit being set in the field. */
+
+ wanted_code = (code == TRUTH_AND_EXPR ? EQ_EXPR : NE_EXPR);
+ if (lcode != wanted_code)
+ {
+ if (l_const.get_precision ()
+ && l_const == 0
+ && ll_and_mask.get_precision ()
+ && wi::popcount (ll_and_mask) == 1)
+ {
+ /* Make the left operand unsigned, since we are only interested
+ in the value of one bit. Otherwise we are doing the wrong
+ thing below. */
+ ll_unsignedp = 1;
+ l_const = ll_and_mask;
+ }
+ else
+ return 0;
+ }
+
+ /* This is analogous to the code for l_const above. */
+ if (rcode != wanted_code)
+ {
+ if (r_const.get_precision ()
+ && r_const == 0
+ && rl_and_mask.get_precision ()
+ && wi::popcount (rl_and_mask) == 1)
+ {
+ rl_unsignedp = 1;
+ r_const = rl_and_mask;
+ }
+ else
+ return 0;
+ }
+
+ /* This will be bumped to 2 if any of the field pairs crosses an
+ alignment boundary, so the merged compare has to be done in two
+ parts. */
+ int parts = 1;
+ /* Set to true if the second combined compare should come first,
+ e.g., because the second original compare accesses a word that
+ the first one doesn't, and the combined compares access those in
+ cmp[0]. */
+ bool first1 = false;
+ /* Set to true if the first original compare is not the one being
+ split. */
+ bool maybe_separate = false;
+
+ /* The following 2-dimensional arrays use the first index to
+ identify left(0)- vs right(1)-hand compare operands, and the
+ second one to identify merged compare parts. */
+ /* The memory loads or constants to be compared. */
+ tree ld_arg[2][2];
+ /* The first bit of the corresponding inner object that the
+ corresponding LD_ARG covers. */
+ HOST_WIDE_INT bitpos[2][2];
+ /* The bit count starting at BITPOS that the corresponding LD_ARG
+ covers. */
+ HOST_WIDE_INT bitsiz[2][2];
+ /* The number of bits by which LD_ARG has already been shifted
+ right, WRT mask. */
+ HOST_WIDE_INT shifted[2][2];
+ /* The number of bits by which both LD_ARG and MASK need shifting to
+ bring its least-significant bit to bit zero. */
+ HOST_WIDE_INT toshift[2][2];
+ /* An additional mask to be applied to LD_ARG, to remove any bits
+ that may have been loaded for use in another compare, but that
+ don't belong in the corresponding compare. */
+ wide_int xmask[2][2] = {};
+
+ /* The combined compare or compares. */
+ tree cmp[2];
+
+ /* Consider we're comparing two non-contiguous fields of packed
+ structs, both aligned at 32-bit boundaries:
+
+ ll_arg: an 8-bit field at offset 0
+ lr_arg: a 16-bit field at offset 2
+
+ rl_arg: an 8-bit field at offset 1
+ rr_arg: a 16-bit field at offset 3
+
+ We'll have r_split_load, because rr_arg straddles across an
+ alignment boundary.
+
+ We'll want to have:
+
+ bitpos = { { 0, 0 }, { 0, 32 } }
+ bitsiz = { { 32, 32 }, { 32, 8 } }
+
+ And, for little-endian:
+
+ shifted = { { 0, 0 }, { 0, 32 } }
+ toshift = { { 0, 24 }, { 0, 0 } }
+
+ Or, for big-endian:
+
+ shifted = { { 0, 0 }, { 8, 0 } }
+ toshift = { { 8, 0 }, { 0, 0 } }
+ */
+
+ /* See if we can find a mode that contains both fields being compared on
+ the left. If we can't, fail. Otherwise, update all constants and masks
+ to be relative to a field of that size. */
+ first_bit = MIN (ll_bitpos, rl_bitpos);
+ end_bit = MAX (ll_bitpos + ll_bitsize, rl_bitpos + rl_bitsize);
+ if (get_best_mode (end_bit - first_bit, first_bit, 0, 0,
+ TYPE_ALIGN (TREE_TYPE (ll_inner)), BITS_PER_WORD,
+ volatilep, &lnmode))
+ l_split_load = false;
+ else
+ {
+ /* Consider the possibility of recombining loads if any of the
+ fields straddles across an alignment boundary, so that either
+ part can be loaded along with the other field. */
+ HOST_WIDE_INT align = TYPE_ALIGN (TREE_TYPE (ll_inner));
+ HOST_WIDE_INT boundary = compute_split_boundary_from_align
+ (align, ll_bitpos, ll_bitsize, rl_bitpos, rl_bitsize);
+
+ if (boundary < 0
+ || !get_best_mode (boundary - first_bit, first_bit, 0, 0,
+ align, BITS_PER_WORD, volatilep, &lnmode)
+ || !get_best_mode (end_bit - boundary, boundary, 0, 0,
+ align, BITS_PER_WORD, volatilep, &lnmode2))
+ return 0;
+
+ /* If we can't have a single load, but can with two, figure out whether
+ the two compares can be separated, i.e., whether the entirety of the
+ first original compare is encompassed by the entirety of the first
+ combined compare. If the first original compare is past the alignment
+ boundary, arrange to compare that range first, by setting first1
+ (meaning make cmp[1] first, instead of cmp[0]). */
+ l_split_load = true;
+ parts = 2;
+ if (ll_bitpos >= boundary)
+ maybe_separate = first1 = true;
+ else if (ll_bitpos + ll_bitsize <= boundary)
+ maybe_separate = true;
+ }
+
+ lnbitsize = GET_MODE_BITSIZE (lnmode);
+ lnbitpos = first_bit & ~ (lnbitsize - 1);
+ /* Avoid situations that the code below can't handle. */
+ if (lnbitpos < 0)
+ return 0;
+
+ /* Choose the type for the combined compare. Even if we're splitting loads,
+ make it wide enough to hold both. */
+ if (l_split_load)
+ lnbitsize += GET_MODE_BITSIZE (lnmode2);
+ lntype = build_nonstandard_integer_type (lnbitsize, 1);
+ if (!lntype)
+ return NULL_TREE;
+ lnprec = TYPE_PRECISION (lntype);
+ xll_bitpos = ll_bitpos - lnbitpos, xrl_bitpos = rl_bitpos - lnbitpos;
+
+ /* Adjust bit ranges for reverse endianness. */
+ if (ll_reversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+ {
+ xll_bitpos = lnbitsize - xll_bitpos - ll_bitsize;
+ xrl_bitpos = lnbitsize - xrl_bitpos - rl_bitsize;
+ }
+
+ /* Adjust masks to match the positions in the combined lntype. */
+ wide_int ll_mask, rl_mask, r_mask;
+ if (ll_and_mask.get_precision ())
+ ll_mask = wi::lshift (wide_int::from (ll_and_mask, lnprec, UNSIGNED),
+ xll_bitpos);
+ else
+ ll_mask = wi::shifted_mask (xll_bitpos, ll_bitsize, false, lnprec);
+ if (rl_and_mask.get_precision ())
+ rl_mask = wi::lshift (wide_int::from (rl_and_mask, lnprec, UNSIGNED),
+ xrl_bitpos);
+ else
+ rl_mask = wi::shifted_mask (xrl_bitpos, rl_bitsize, false, lnprec);
+
+ /* Adjust right-hand constants in both original comparisons to match width
+ and bit position. */
+ if (l_const.get_precision ())
+ {
+ /* We're doing bitwise equality tests, so don't bother with sign
+ extensions. */
+ l_const = wide_int::from (l_const, lnprec, UNSIGNED);
+ if (ll_and_mask.get_precision ())
+ l_const &= wide_int::from (ll_and_mask, lnprec, UNSIGNED);
+ l_const <<= xll_bitpos;
+ if ((l_const & ~ll_mask) != 0)
+ {
+ warning_at (lloc, OPT_Wtautological_compare,
+ "comparison is always %d", wanted_code == NE_EXPR);
+
+ return constant_boolean_node (wanted_code == NE_EXPR, truth_type);
+ }
+
+ /* When we set l_const, we also set r_const, so we need not test it
+ again. */
+ gcc_checking_assert (r_const.get_precision ());
+
+ r_const = wide_int::from (r_const, lnprec, UNSIGNED);
+ if (rl_and_mask.get_precision ())
+ r_const &= wide_int::from (rl_and_mask, lnprec, UNSIGNED);
+ r_const <<= xrl_bitpos;
+ if ((r_const & ~rl_mask) != 0)
+ {
+ warning_at (rloc, OPT_Wtautological_compare,
+ "comparison is always %d", wanted_code == NE_EXPR);
+
+ return constant_boolean_node (wanted_code == NE_EXPR, truth_type);
+ }
+
+ /* If there is something in common between the masks, those bits of the
+ constants must be the same. If not, the combined condition cannot be
+ met, and the result is known. Test for this to avoid generating
+ incorrect code below. */
+ wide_int mask = ll_mask & rl_mask;
+ if (mask != 0
+ && (l_const & mask) != (r_const & mask))
+ {
+ if (wanted_code == NE_EXPR)
+ return constant_boolean_node (true, truth_type);
+ else
+ return constant_boolean_node (false, truth_type);
+ }
+
+ /* The constants are combined so as to line up with the loaded field, so
+ tentatively use the same parameters for the second combined
+ compare. */
+ ld_arg[1][0] = wide_int_to_tree (lntype, l_const | r_const);
+ toshift[1][0] = MIN (xll_bitpos, xrl_bitpos);
+ shifted[1][0] = 0;
+ bitpos[1][0] = lnbitpos;
+ bitsiz[1][0] = lnbitsize;
+
+ if (parts > 1)
+ reuse_split_load (ld_arg[1], bitpos[1], bitsiz[1], toshift[1],
+ shifted[1], xmask[1],
+ lnbitpos + GET_MODE_BITSIZE (lnmode),
+ lr_reversep);
+
+ /* No masking needed, we know the full constants. */
+ r_mask = wi::mask (0, true, lnprec);
+
+ /* If the compiler thinks this is used uninitialized below, it's
+ because it can't realize that parts can only be 2 when
+ comparing with constants if l_split_load is also true. This
+ just silences the warning. */
+ rnbitpos = 0;
+ }
+
+ /* Likewise, if the right sides are not constant, align them for the combined
+ compare. Also, disallow this optimization if a size, signedness or
+ storage order mismatch occurs between the left and right sides. */
+ else
+ {
+ if (ll_bitsize != lr_bitsize || rl_bitsize != rr_bitsize
+ || ll_unsignedp != lr_unsignedp || rl_unsignedp != rr_unsignedp
+ || ll_reversep != lr_reversep
+ /* Make sure the two fields on the right
+ correspond to the left without being swapped. */
+ || ll_bitpos - rl_bitpos != lr_bitpos - rr_bitpos)
+ return 0;
+
+ bool r_split_load;
+ scalar_int_mode rnmode2;
+
+ /* Figure out how to load the bits for the right-hand size of the
+ combined compare. As in the left-hand size, we may have to split it,
+ and then we use two separate compares. */
+ first_bit = MIN (lr_bitpos, rr_bitpos);
+ end_bit = MAX (lr_bitpos + lr_bitsize, rr_bitpos + rr_bitsize);
+ if (!get_best_mode (end_bit - first_bit, first_bit, 0, 0,
+ TYPE_ALIGN (TREE_TYPE (lr_inner)), BITS_PER_WORD,
+ volatilep, &rnmode))
+ {
+ /* Consider the possibility of recombining loads if any of the
+ fields straddles across an alignment boundary, so that either
+ part can be loaded along with the other field. */
+ HOST_WIDE_INT align = TYPE_ALIGN (TREE_TYPE (lr_inner));
+ HOST_WIDE_INT boundary = compute_split_boundary_from_align
+ (align, lr_bitpos, lr_bitsize, rr_bitpos, rr_bitsize);
+
+ if (boundary < 0
+ /* If we're to split both, make sure the split point is
+ the same. */
+ || (l_split_load
+ && (boundary - lr_bitpos
+ != (lnbitpos + GET_MODE_BITSIZE (lnmode)) - ll_bitpos))
+ || !get_best_mode (boundary - first_bit, first_bit, 0, 0,
+ align, BITS_PER_WORD, volatilep, &rnmode)
+ || !get_best_mode (end_bit - boundary, boundary, 0, 0,
+ align, BITS_PER_WORD, volatilep, &rnmode2))
+ return 0;
+
+ r_split_load = true;
+ parts = 2;
+ if (lr_bitpos >= boundary)
+ maybe_separate = first1 = true;
+ else if (lr_bitpos + lr_bitsize <= boundary)
+ maybe_separate = true;
+ }
+ else
+ r_split_load = false;
+
+ /* Find a type that can hold the entire right-hand operand. */
+ rnbitsize = GET_MODE_BITSIZE (rnmode);
+ rnbitpos = first_bit & ~ (rnbitsize - 1);
+ if (r_split_load)
+ rnbitsize += GET_MODE_BITSIZE (rnmode2);
+ rntype = build_nonstandard_integer_type (rnbitsize, 1);
+ if (!rntype)
+ return 0;
+ rnprec = TYPE_PRECISION (rntype);
+ xlr_bitpos = lr_bitpos - rnbitpos, xrr_bitpos = rr_bitpos - rnbitpos;
+
+ /* Adjust for reversed endianness. */
+ if (lr_reversep ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+ {
+ xlr_bitpos = rnbitsize - xlr_bitpos - lr_bitsize;
+ xrr_bitpos = rnbitsize - xrr_bitpos - rr_bitsize;
+ }
+
+ /* Adjust the masks to match the combined type, and combine them. */
+ wide_int lr_mask, rr_mask;
+ if (lr_and_mask.get_precision ())
+ lr_mask = wi::lshift (wide_int::from (lr_and_mask, rnprec, UNSIGNED),
+ xlr_bitpos);
+ else
+ lr_mask = wi::shifted_mask (xlr_bitpos, lr_bitsize, false, rnprec);
+ if (rl_and_mask.get_precision ())
+ rr_mask = wi::lshift (wide_int::from (rr_and_mask, rnprec, UNSIGNED),
+ xrr_bitpos);
+ else
+ rr_mask = wi::shifted_mask (xrr_bitpos, rr_bitsize, false, rnprec);
+ r_mask = lr_mask | rr_mask;
+
+ /* Load the right-hand operand of the combined compare. */
+ toshift[1][0] = MIN (xlr_bitpos, xrr_bitpos);
+ shifted[1][0] = 0;
+
+ if (!r_split_load)
+ {
+ bitpos[1][0] = rnbitpos;
+ bitsiz[1][0] = rnbitsize;
+ ld_arg[1][0] = make_bit_field_load (ll_loc[3], lr_inner, lr_arg,
+ rntype, rnbitsize, rnbitpos,
+ lr_unsignedp || rr_unsignedp,
+ lr_reversep, lr_load);
+ }
+
+ /* ... and the second part of the right-hand operand if needed. */
+ if (parts > 1)
+ {
+ if (r_split_load)
+ {
+ gimple *point[2];
+ point[0] = lr_load;
+ point[1] = rr_load;
+ build_split_load (ld_arg[1], bitpos[1], bitsiz[1], toshift[1],
+ shifted[1], rl_loc[3], lr_inner, lr_arg,
+ rnmode, rnmode2, rnbitpos, lr_reversep, point);
+ }
+ else
+ reuse_split_load (ld_arg[1], bitpos[1], bitsiz[1], toshift[1],
+ shifted[1], xmask[1],
+ lnbitpos + GET_MODE_BITSIZE (lnmode)
+ - ll_bitpos + lr_bitpos, lr_reversep);
+ }
+ }
+
+ /* Now issue the loads for the left-hand combined operand/s. */
+ wide_int l_mask = ll_mask | rl_mask;
+ toshift[0][0] = MIN (xll_bitpos, xrl_bitpos);
+ shifted[0][0] = 0;
+
+ if (!l_split_load)
+ {
+ bitpos[0][0] = lnbitpos;
+ bitsiz[0][0] = lnbitsize;
+ ld_arg[0][0] = make_bit_field_load (ll_loc[3], ll_inner, ll_arg,
+ lntype, lnbitsize, lnbitpos,
+ ll_unsignedp || rl_unsignedp,
+ ll_reversep, ll_load);
+ }
+
+ if (parts > 1)
+ {
+ if (l_split_load)
+ {
+ gimple *point[2];
+ point[0] = ll_load;
+ point[1] = rl_load;
+ build_split_load (ld_arg[0], bitpos[0], bitsiz[0], toshift[0],
+ shifted[0], rl_loc[3], ll_inner, ll_arg,
+ lnmode, lnmode2, lnbitpos, ll_reversep, point);
+ }
+ else
+ reuse_split_load (ld_arg[0], bitpos[0], bitsiz[0], toshift[0],
+ shifted[0], xmask[0],
+ rnbitpos + GET_MODE_BITSIZE (rnmode)
+ - lr_bitpos + ll_bitpos, ll_reversep);
+ }
+
+ /* Compute the compares. */
+ for (int i = 0; i < parts; i++)
+ {
+ tree op[2] = { ld_arg[0][i], ld_arg[1][i] };
+ wide_int mask[2] = { l_mask, r_mask };
+ location_t *locs[2] = { i ? rl_loc : ll_loc, i ? rr_loc : lr_loc };
+
+ /* Figure out the masks, and unshare the original operands. */
+ for (int j = 0; j < 2; j++)
+ {
+ unsigned prec = TYPE_PRECISION (TREE_TYPE (op[j]));
+ op[j] = unshare_expr (op[j]);
+
+ /* Mask out the bits belonging to the other part. */
+ if (xmask[j][i].get_precision ())
+ mask[j] &= xmask[j][i];
+
+ if (shifted[j][i])
+ {
+ wide_int shift = wide_int::from (shifted[j][i], prec, UNSIGNED);
+ mask[j] = wi::lrshift (mask[j], shift);
+ }
+ mask[j] = wide_int::from (mask[j], prec, UNSIGNED);
+ }
+
+ /* Line up the operands for a compare. */
+ HOST_WIDE_INT shift = (toshift[0][i] - toshift[1][i]);
+
+ if (shift)
+ {
+ int j;
+ if (shift > 0)
+ j = 0;
+ else
+ {
+ j = 1;
+ shift = -shift;
+ }
+
+ tree shiftsz = bitsize_int (shift);
+ op[j] = fold_build2_loc (locs[j][1], RSHIFT_EXPR, TREE_TYPE (op[j]),
+ op[j], shiftsz);
+ mask[j] = wi::lrshift (mask[j], shift);
+ }
+
+ /* Convert to the smaller type before masking out unwanted
+ bits. */
+ tree type = TREE_TYPE (op[0]);
+ if (type != TREE_TYPE (op[1]))
+ {
+ int j = (TYPE_PRECISION (type)
+ < TYPE_PRECISION (TREE_TYPE (op[1])));
+ if (!j)
+ type = TREE_TYPE (op[1]);
+ op[j] = fold_convert_loc (locs[j][0], type, op[j]);
+ mask[j] = wide_int::from (mask[j], TYPE_PRECISION (type), UNSIGNED);
+ }
+
+ /* Apply masks. */
+ for (int j = 0; j < 2; j++)
+ if (mask[j] != wi::mask (0, true, mask[j].get_precision ()))
+ op[j] = build2_loc (locs[j][2], BIT_AND_EXPR, type,
+ op[j], wide_int_to_tree (type, mask[j]));
+
+ cmp[i] = build2_loc (i ? rloc : lloc, wanted_code, truth_type,
+ op[0], op[1]);
+ }
+
+ /* Reorder the compares if needed. */
+ if (first1)
+ std::swap (cmp[0], cmp[1]);
+
+ /* Prepare to return the resulting compares. Combine two parts if
+ needed. */
+ if (parts == 1)
+ result = cmp[0];
+ else if (!separatep || !maybe_separate)
+ result = build2_loc (rloc, orig_code, truth_type, cmp[0], cmp[1]);
+ else
+ {
+ result = cmp[0];
+ *separatep = cmp[1];
+ }
+
+ return result;
+}
+
/* Try to simplify the AND of two comparisons, specified by
(OP1A CODE1 OP1B) and (OP2B CODE2 OP2B), respectively.
If this can be simplified to a single expression (without requiring
new file mode 100644
@@ -0,0 +1,64 @@
+/* { dg-do run } */
+/* { dg-options "-O -save-temps -fdump-tree-optimized" } */
+
+/* Check that field loads compared with constants are merged, even if
+ tested out of order, and when fields straddle across alignment
+ boundaries. */
+
+struct TL {
+ unsigned char p;
+ unsigned int a;
+ unsigned char q;
+ unsigned int b;
+ unsigned char r;
+ unsigned int c;
+ unsigned char s;
+} __attribute__ ((packed, aligned (4), scalar_storage_order ("little-endian")));
+
+struct TB {
+ unsigned char p;
+ unsigned int a;
+ unsigned char q;
+ unsigned int b;
+ unsigned char r;
+ unsigned int c;
+ unsigned char s;
+} __attribute__ ((packed, aligned (4), scalar_storage_order ("big-endian")));
+
+#define vc 0xaa
+#define vi 0x12345678
+
+struct TL vL = { vc, vi, vc, vi, vc, vi, vc };
+struct TB vB = { vc, vi, vc, vi, vc, vi, vc };
+
+void f (void) {
+ /* Which words of | vL | vB | */
+ /* are accessed by |0123|0123| */
+ if (0 /* the tests? | | | */
+ || vL.p != vc /* |* | | */
+ || vB.p != vc /* | |* | */
+ || vL.s != vc /* | *| | */
+ || vB.q != vc /* | | * | */
+ || vL.a != vi /* |^* | | */
+ || vB.b != vi /* | | ^* | */
+ || vL.c != vi /* | *^| | */
+ || vB.c != vi /* | | ^*| */
+ || vL.b != vi /* | ^^ | | */
+ || vL.q != vc /* | ^ | | */
+ || vB.a != vi /* | |^^ | */
+ || vB.r != vc /* | | ^ | */
+ || vB.s != vc /* | | ^| */
+ || vL.r != vc /* | ^ | | */
+ )
+ __builtin_abort ();
+}
+
+int main () {
+ f ();
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "BIT_FIELD_REF" 8 "optimized" } } */
+/* { dg-final { scan-assembler-not "cmpb" { target { i*86-*-* || x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "cmpl" 8 { target { i*86-*-* || x86_64-*-* } } } } */
+/* { dg-final { scan-assembler-times "cmpw" 8 { target { powerpc*-*-* || rs6000-*-* } } } } */
new file mode 100644
@@ -0,0 +1,36 @@
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+/* Check that we don't move loads across stores. */
+
+struct s {
+ short a;
+ short b;
+} __attribute__ ((aligned (4)));
+
+struct s p[2] = {
+ { 42, 0xfe01 },
+ { 42, 0xfe10 }
+};
+
+void f (void) {
+ short a0 = p[0].a;
+ short b0 = p[0].b;
+ short a1 = p[1].a;
+ short b1 = p[1].b;
+ __builtin_memset (p, 0, sizeof (p));
+ asm ("" : "+m" (p));
+ if (0
+ || p[0].a != p[1].a
+ || p[0].b != p[1].b
+ )
+ __builtin_abort ();
+ if (a0 == a1)
+ if (b0 == b1)
+ __builtin_abort ();
+}
+
+int main () {
+ f ();
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+/* Check that narrowing casts aren't ignored, and that same-field tests at
+ different widths aren't misoptimized. */
+
+struct s {
+ short a;
+ unsigned short b;
+ int c;
+} __attribute__ ((aligned (4)));
+
+struct s p = { 42, (short)(0xef1 - 0x1000), 0x12345678 };
+
+void f (void) {
+ if (0
+ || (p.a & 0xcc) != 8
+ || p.a != 42
+ || (int)(signed char)p.b != (int)(signed char)(0xef1 - 0x1000)
+ || (unsigned)(unsigned char)p.b != (unsigned)(unsigned char)(0xef1 - 0x1000)
+ || (unsigned)p.b != (unsigned short)(0xef1 - 0x1000)
+ || (int)(short)p.b != (int)(0xef1 - 0x1000)
+ || (long)(unsigned char)(p.c >> 8) != (long)(unsigned char)0x123456
+ || p.c != 0x12345678
+ )
+ __builtin_abort ();
+}
+
+int main () {
+ f ();
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,33 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+/* Check that we don't crash when trying to handle masks that don't match the
+ width of the original type. */
+
+struct s {
+ long long q;
+};
+
+struct s x1 = { 1 };
+struct s xm1 = { -1 };
+struct s x8 = { 8 };
+struct s x0 = { 0 };
+
+bool f(struct s *p)
+{
+ int q = (int)p->q;
+ return (q < 0) || (q & 7);
+}
+
+int main ()
+{
+ if (!f (&x1))
+ __builtin_abort ();
+ if (!f (&xm1))
+ __builtin_abort ();
+ if (f (&x8))
+ __builtin_abort ();
+ if (f (&x0))
+ __builtin_abort ();
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+struct TL {
+ unsigned short a;
+ unsigned short b;
+} __attribute__ ((packed, aligned (8)));
+
+struct TB {
+ unsigned char p;
+ unsigned short a;
+ unsigned short b;
+} __attribute__ ((packed, aligned (8)));
+
+#define vc 0xaa
+
+struct TL vL = { vc, vc };
+struct TB vB = { vc, vc, vc };
+
+void f (void) {
+ if (0
+ || vL.b != vB.b
+ || vL.a != vB.a
+ )
+ __builtin_abort ();
+}
+
+int main () {
+ f ();
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,36 @@
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+const int BIG_ENDIAN_P = (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__);
+
+struct T1 {
+ unsigned char p[2];
+ unsigned short a;
+ unsigned int z;
+} __attribute__((__aligned__(8)));
+
+struct T2 {
+ unsigned short p;
+ unsigned short a;
+ unsigned int z;
+} __attribute__((__aligned__(8)));
+
+#define vc 0xaa
+#define vi 0x12345678
+
+struct T1 v1 = { { vc + !BIG_ENDIAN_P, vc + BIG_ENDIAN_P }, vc, vi };
+struct T2 v2 = { (vc << 8) | (vc - 1), vc, vi };
+
+void f (void) {
+ if (0
+ || v1.p[!BIG_ENDIAN_P] != v2.p >> 8
+ || v1.a != v2.a
+ || ((v1.z ^ v2.z) & 0xff00ff00) != 0
+ || ((v1.z ^ v2.z) & 0x00ff00ff) != 0)
+ __builtin_abort ();
+}
+
+int main () {
+ f ();
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,40 @@
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+struct T1 {
+ unsigned int zn;
+ unsigned char p;
+ unsigned char qn;
+ unsigned short a;
+ unsigned int z;
+} __attribute__((__packed__, __aligned__(4)));
+
+struct T2 {
+ unsigned int zn;
+ unsigned char rn;
+ unsigned char p;
+ unsigned char qn;
+ unsigned short a;
+ unsigned int z;
+} __attribute__((__packed__, __aligned__(4)));
+
+#define vc 0xaa
+#define vs 0xccdd
+#define vi 0x12345678
+
+struct T1 v1 = { -1, vc, 1, vs, vi };
+struct T2 v2 = { -1, 0, vc, 1, vs, vi };
+
+void f (void) {
+ if (0
+ || v1.p != v2.p
+ || v1.a != v2.a
+ || v1.z != v2.z
+ )
+ __builtin_abort ();
+}
+
+int main () {
+ f ();
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,40 @@
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+struct T1 {
+ unsigned int zn;
+ unsigned char p;
+ unsigned char qn;
+ unsigned short a;
+ unsigned int z;
+} __attribute__((__packed__, __aligned__(8)));
+
+struct T2 {
+ unsigned int zn;
+ unsigned char rn;
+ unsigned char p;
+ unsigned char qn;
+ unsigned short a;
+ unsigned int z;
+} __attribute__((__packed__, __aligned__(8)));
+
+#define vc 0xaa
+#define vs 0xccdd
+#define vi 0x12345678
+
+struct T1 v1 = { -1, vc, 1, vs, vi };
+struct T2 v2 = { -1, 0, vc, 1, vs, vi };
+
+void f (void) {
+ if (0
+ || v1.p != v2.p
+ || v1.a != v2.a
+ || v1.z != v2.z
+ )
+ __builtin_abort ();
+}
+
+int main () {
+ f ();
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,26 @@
+/* { dg-do run } */
+/* { dg-options "-O" } */
+/* { dg-shouldfail } */
+
+/* Check that the third compare won't be pulled ahead of the second one and
+ prevent, which would prevent the NULL pointer dereference that should cause
+ the execution to fail. */
+
+struct s {
+ char a, b;
+ int *p;
+};
+
+struct s a = { 0, 1, 0 };
+struct s b = { 0, 0, 0 };
+
+int f () {
+ return (a.a != b.a
+ || *b.p != *a.p
+ || a.b != b.b);
+}
+
+int main() {
+ f ();
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdump-tree-ifcombine-details" } */
+
+/* Check that the third compare won't be combined with the first one. */
+
+struct s {
+ char a, b;
+ int p;
+};
+
+struct s a = { 0, 0, 0 };
+struct s b = { 0, 0, 0 };
+
+int f () {
+ return (a.a != b.a || (a.p != b.p && a.b != b.b));
+}
+
+int g () {
+ return (a.a == b.a && (a.p == b.p || a.b == b.b));
+}
+
+/* { dg-final { scan-tree-dump-not "optimizing" "ifcombine" } } */
+/* { dg-final { scan-tree-dump-not "BIT_FIELD_REF" "ifcombine" } } */
new file mode 100644
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/* { dg-options "-O" } */
+
+/* Check that conversions are not thrown away. */
+
+struct s {
+ unsigned char a;
+ unsigned short b;
+} __attribute__ ((aligned (4)));
+
+struct s p = { 42, 0xfe };
+struct s q = { 42, 0xfe | (2 << (__CHAR_BIT__ - 1)) };
+
+void f (void) {
+ if (0
+ || p.a != q.a
+ || (unsigned char)p.b != (unsigned char)q.b
+ )
+ __builtin_abort ();
+}
+
+int main () {
+ f ();
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,38 @@
+/* { dg-do run } */
+/* { dg-options "-O -fdump-tree-ifcombine-details" } */
+
+/* Check that conversions and selections of similar bit ranges across different
+ types don't prevent combination. */
+
+struct s1 {
+ unsigned char b[2];
+ unsigned char a;
+} __attribute__ ((aligned (4)));
+
+struct s2 {
+ unsigned short b;
+ unsigned char a;
+} __attribute__ ((aligned (4)));
+
+static const char le = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ? 1 : 0;
+
+struct s1 p = { { -!le , -le }, 42 };
+struct s2 q = { (le
+ ? -2 << (__CHAR_BIT__ - 1)
+ : -1 & ((1 << (__CHAR_BIT__ - 1) << 1) - 1)), 42 };
+
+void f (void) {
+ if (0
+ || p.a != q.a
+ || p.b[!le] != (unsigned char)q.b
+ || p.b[le] != (unsigned char)((q.b >> (__CHAR_BIT__ - 1)) >> 1)
+ )
+ __builtin_abort ();
+}
+
+int main () {
+ f ();
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "optimizing two comparisons" 2 "ifcombine" } } */
@@ -971,7 +971,19 @@ ifcombine_ifandif (basic_block inner_cond_bb, bool inner_inv,
outer_cond_code,
gimple_cond_lhs (outer_cond),
gimple_cond_rhs (outer_cond),
- gimple_bb (outer_cond))))
+ gimple_bb (outer_cond)))
+ && !(t = (fold_truth_andor_for_ifcombine
+ (TRUTH_ANDIF_EXPR, boolean_type_node,
+ gimple_location (outer_cond),
+ outer_cond_code,
+ gimple_cond_lhs (outer_cond),
+ gimple_cond_rhs (outer_cond),
+ gimple_location (inner_cond),
+ inner_cond_code,
+ gimple_cond_lhs (inner_cond),
+ gimple_cond_rhs (inner_cond),
+ single_pred (inner_cond_bb) != outer_cond_bb
+ ? &ts : 0))))
{
/* Only combine conditions in this fallback case if the blocks are
neighbors. */