2025-01-09 Jakub Jelinek PR tree-optimization/118206 * gimple-fold.cc (fold_truth_andor_for_ifcombine): Temporarily disable. * fold-const.cc (decode_field_reference, all_ones_mask_p, unextend, fold_truth_andor_1): Temporarily revert 2024-12-12 changes. --- gcc/gimple-fold.cc.jj 2025-01-02 11:23:09.379616499 +0100 +++ gcc/gimple-fold.cc 2025-01-09 09:08:53.789628193 +0100 @@ -8037,7 +8037,8 @@ fold_truth_andor_for_ifcombine (enum tre it were surrounded with a NE_EXPR. */ if (TREE_CODE_CLASS (lcode) != tcc_comparison - || TREE_CODE_CLASS (rcode) != tcc_comparison) + || TREE_CODE_CLASS (rcode) != tcc_comparison + || 1) return 0; /* We don't normally find TRUTH_*IF_EXPR in gimple, but these codes may be --- gcc/fold-const.cc.jj +++ gcc/fold-const.cc @@ -137,6 +137,7 @@ 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, @@ -5021,6 +5022,136 @@ 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. @@ -6330,6 +6461,48 @@ 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 @@ -6400,13 +6573,20 @@ 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. @@ -6431,9 +6611,24 @@ 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 result; + 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; /* 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 @@ -6528,7 +6723,316 @@ fold_truth_andor_1 (location_t loc, enum tree_code code, tree truth_type, build_int_cst (TREE_TYPE (ll_arg), 0)); } - return 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, "% of unmatched not-equal tests is always 1"); + return constant_boolean_node (true, truth_type); + } + else + { + warning (0, "% 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)); } /* T is an integer expression that is being multiplied, divided, or taken a