On Thu, Dec 5, 2024 at 1:39 PM Alexandre Oliva <[email protected]> wrote:
>
> Hi,
>
> Thanks for the review.
>
> I hadn't seen your response yet, and for days I have been just about to
> send v3 with some incremental changes.
>
> Here they are, for the record. They fix a bug I had introduced while
> converting constants and masks to wide_int, simplifies some internal
> APIs, reworks some variable naming, ensures right-hand masks are
> reapplied where in some cases they weren't, drops other unnecessary
> masks, and simplifies the handling of mask and constant widths to
> disregard signedness, since at that point we've already checked
> signedness and we're interested in known bit ranges, so sign extension
> is just not necessary.
>
> I'll hold off from posting the v3 I was just about to post, implement
> the changes recommended in your response and then post another
> incremental, for the record, and the resulting v3.
The incremental changes look good, I have nothing to add there
to the review of v2.
Thanks,
Richard.
> Thanks,
>
>
> diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
> index 149df985bee41..2c33cdfb1b29a 100644
> --- a/gcc/gimple-fold.cc
> +++ b/gcc/gimple-fold.cc
> @@ -7446,9 +7446,6 @@ maybe_fold_comparisons_from_match_pd (tree type, enum
> tree_code code,
> *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.
>
> @@ -7456,10 +7453,8 @@ maybe_fold_comparisons_from_match_pd (tree type, enum
> tree_code code,
>
> *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.
> + 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,
> @@ -7478,10 +7473,9 @@ maybe_fold_comparisons_from_match_pd (tree type, enum
> tree_code code,
>
> static tree
> decode_field_reference (tree *pexp, HOST_WIDE_INT *pbitsize,
> - HOST_WIDE_INT *pbitpos, machine_mode *pmode,
> + HOST_WIDE_INT *pbitpos,
> bool *punsignedp, bool *preversep, bool *pvolatilep,
> - wide_int *pmask, wide_int *pand_mask,
> - bool *xor_p, tree *xor_cmp_op,
> + wide_int *pand_mask, bool *xor_p, tree *xor_cmp_op,
> gimple **load, location_t loc[4])
> {
> /* These are from match.pd. */
> @@ -7494,10 +7488,9 @@ decode_field_reference (tree *pexp, HOST_WIDE_INT
> *pbitsize,
> tree outer_type = 0;
> wide_int and_mask;
> tree inner, offset;
> - unsigned int precision;
> int shiftrt = 0;
> - wide_int mask;
> tree res_ops[2];
> + machine_mode mode;
>
> *load = NULL;
>
> @@ -7522,7 +7515,7 @@ decode_field_reference (tree *pexp, HOST_WIDE_INT
> *pbitsize,
> }
>
> /* Recognize and save a masking operation. */
> - if (gimple_bit_and_cst (exp, res_ops, follow_all_ssa_edges))
> + if (pand_mask && gimple_bit_and_cst (exp, res_ops, follow_all_ssa_edges))
> {
> loc[1] = gimple_location (SSA_NAME_DEF_STMT (exp));
> exp = res_ops[0];
> @@ -7600,11 +7593,10 @@ decode_field_reference (tree *pexp, HOST_WIDE_INT
> *pbitsize,
> poly_int64 poly_bitsize, poly_bitpos;
> int unsignedp, reversep = *preversep, volatilep = *pvolatilep;
> inner = get_inner_reference (exp, &poly_bitsize, &poly_bitpos, &offset,
> - pmode, &unsignedp, &reversep, &volatilep);
> + &mode, &unsignedp, &reversep, &volatilep);
>
> HOST_WIDE_INT bs, bp;
> - if ((inner == exp && !and_mask.get_precision ())
> - || !poly_bitsize.is_constant (&bs)
> + if (!poly_bitsize.is_constant (&bs)
> || !poly_bitpos.is_constant (&bp)
> || bs <= shiftrt
> || offset != 0
> @@ -7646,17 +7638,13 @@ decode_field_reference (tree *pexp, HOST_WIDE_INT
> *pbitsize,
> if (outer_type && *pbitsize == TYPE_PRECISION (outer_type))
> *punsignedp = TYPE_UNSIGNED (outer_type);
>
> - /* Compute the mask to access the bitfield. */
> - precision = *pbitsize;
> -
> - mask = wi::mask (*pbitsize, false, precision);
> -
> - /* Merge it with the mask we found in the BIT_AND_EXPR, if any. */
> + /* Make the mask the expected width. */
> if (and_mask.get_precision () != 0)
> - mask &= wide_int::from (and_mask, precision, UNSIGNED);
> + and_mask = wide_int::from (and_mask, *pbitsize, UNSIGNED);
> +
> + if (pand_mask)
> + *pand_mask = and_mask;
>
> - *pmask = mask;
> - *pand_mask = and_mask;
> return inner;
> }
>
> @@ -7913,9 +7901,7 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> HOST_WIDE_INT rnbitsize, rnbitpos, rnprec;
> bool ll_unsignedp, lr_unsignedp, rl_unsignedp, rr_unsignedp;
> bool ll_reversep, lr_reversep, rl_reversep, rr_reversep;
> - machine_mode ll_mode, lr_mode, rl_mode, rr_mode;
> scalar_int_mode lnmode, lnmode2, rnmode;
> - wide_int ll_mask, lr_mask, rl_mask, rr_mask;
> wide_int ll_and_mask, lr_and_mask, rl_and_mask, rr_and_mask;
> wide_int l_const, r_const;
> tree lntype, rntype, result;
> @@ -7987,25 +7973,21 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> 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_mode,
> + ll_inner = decode_field_reference (&ll_arg, &ll_bitsize, &ll_bitpos,
> &ll_unsignedp, &ll_reversep, &volatilep,
> - &ll_mask, &ll_and_mask, &l_xor, &lr_arg,
> + &ll_and_mask, &l_xor, &lr_arg,
> &ll_load, ll_loc);
> - lr_inner = decode_field_reference (&lr_arg,
> - &lr_bitsize, &lr_bitpos, &lr_mode,
> + lr_inner = decode_field_reference (&lr_arg, &lr_bitsize, &lr_bitpos,
> &lr_unsignedp, &lr_reversep, &volatilep,
> - &lr_mask, &lr_and_mask, &l_xor, 0,
> + &lr_and_mask, &l_xor, 0,
> &lr_load, lr_loc);
> - rl_inner = decode_field_reference (&rl_arg,
> - &rl_bitsize, &rl_bitpos, &rl_mode,
> + rl_inner = decode_field_reference (&rl_arg, &rl_bitsize, &rl_bitpos,
> &rl_unsignedp, &rl_reversep, &volatilep,
> - &rl_mask, &rl_and_mask, &r_xor, &rr_arg,
> + &rl_and_mask, &r_xor, &rr_arg,
> &rl_load, rl_loc);
> - rr_inner = decode_field_reference (&rr_arg,
> - &rr_bitsize, &rr_bitpos, &rr_mode,
> + rr_inner = decode_field_reference (&rr_arg, &rr_bitsize, &rr_bitpos,
> &rr_unsignedp, &rr_reversep, &volatilep,
> - &rr_mask, &rr_and_mask, &r_xor, 0,
> + &rr_and_mask, &r_xor, 0,
> &rr_load, rr_loc);
>
> /* It must be true that the inner operation on the lhs of each
> @@ -8024,7 +8006,15 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> && 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
> @@ -8038,12 +8028,7 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
>
> if (lsignbit)
> {
> - wide_int sign = wi::mask (ll_bitsize - 1, true,
> - TYPE_PRECISION (TREE_TYPE (ll_arg)));
> - if (!ll_mask.get_precision ())
> - ll_mask = sign;
> - else
> - ll_mask &= sign;
> + wide_int sign = wi::mask (ll_bitsize - 1, true, ll_bitsize);
> if (!ll_and_mask.get_precision ())
> ll_and_mask = sign;
> else
> @@ -8052,12 +8037,7 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
>
> if (rsignbit)
> {
> - wide_int sign = wi::mask (rl_bitsize - 1, true,
> - TYPE_PRECISION (TREE_TYPE (rl_arg)));
> - if (!rl_mask.get_precision ())
> - rl_mask = sign;
> - else
> - rl_mask &= sign;
> + wide_int sign = wi::mask (rl_bitsize - 1, true, rl_bitsize);
> if (!rl_and_mask.get_precision ())
> rl_and_mask = sign;
> else
> @@ -8073,13 +8053,14 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> {
> if (l_const.get_precision ()
> && l_const == 0
> - && wi::popcount (ll_mask) == 1)
> + && 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_mask;
> + l_const = ll_and_mask;
> }
> else
> return 0;
> @@ -8090,10 +8071,11 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> {
> if (r_const.get_precision ()
> && r_const == 0
> - && wi::popcount (rl_mask) == 1)
> + && rl_and_mask.get_precision ()
> + && wi::popcount (rl_and_mask) == 1)
> {
> rl_unsignedp = 1;
> - r_const = rl_mask;
> + r_const = rl_and_mask;
> }
> else
> return 0;
> @@ -8231,23 +8213,27 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> }
>
> /* Adjust masks to match the positions in the combined lntype. */
> - ll_mask = wi::lshift (wide_int::from (ll_mask, lnprec, UNSIGNED),
> - xll_bitpos);
> - rl_mask = wi::lshift (wide_int::from (rl_mask, lnprec, UNSIGNED),
> - xrl_bitpos);
> + 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 ())
> {
> - l_const = wide_int::from (l_const, lnprec,
> - TYPE_SIGN (TREE_TYPE (lr_arg)));
> - if (!TYPE_UNSIGNED (TREE_TYPE (lr_arg)))
> - {
> - l_const = wi::zext (l_const, TYPE_PRECISION (TREE_TYPE (lr_arg)));
> - if (ll_and_mask.get_precision ())
> - l_const &= wide_int::from (ll_and_mask, lnprec, UNSIGNED);
> - }
> + /* 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)
> {
> @@ -8260,14 +8246,9 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> again. */
> gcc_checking_assert (r_const.get_precision ());
>
> - r_const = wide_int::from (r_const, lnprec,
> - TYPE_SIGN (TREE_TYPE (rr_arg)));
> - if (!TYPE_UNSIGNED (TREE_TYPE (rr_arg)))
> - {
> - r_const = wi::zext (r_const, TYPE_PRECISION (TREE_TYPE (rr_arg)));
> - if (rl_and_mask.get_precision ())
> - r_const &= wide_int::from (rl_and_mask, lnprec, UNSIGNED);
> - }
> + 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)
> {
> @@ -8306,7 +8287,7 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> lr_reversep);
>
> /* No masking needed, we know the full constants. */
> - lr_mask = wi::mask (0, true, lnprec);
> + 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
> @@ -8391,10 +8372,18 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> }
>
> /* Adjust the masks to match the combined type, and combine them. */
> - lr_mask = wide_int::from (lr_mask, rnprec, UNSIGNED) << xlr_bitpos;
> - rr_mask = wide_int::from (rr_mask, rnprec, UNSIGNED) << xrr_bitpos;
> -
> - lr_mask |= rr_mask;
> + 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);
> @@ -8431,7 +8420,7 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> }
>
> /* Now issue the loads for the left-hand combined operand/s. */
> - ll_mask |= rl_mask;
> + wide_int l_mask = ll_mask | rl_mask;
> toshift[0][0] = MIN (xll_bitpos, xrl_bitpos);
> shifted[0][0] = 0;
>
> @@ -8467,7 +8456,7 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> for (int i = 0; i < parts; i++)
> {
> tree op[2] = { ld_arg[0][i], ld_arg[1][i] };
> - wide_int mask[2] = { ll_mask, lr_mask };
> + 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. */
> diff --git a/gcc/testsuite/gcc.dg/field-merge-12.c
> b/gcc/testsuite/gcc.dg/field-merge-12.c
> new file mode 100644
> index 0000000000000..7056eb607e904
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/field-merge-12.c
> @@ -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;
> +}
> diff --git a/gcc/testsuite/gcc.dg/field-merge-9.c
> b/gcc/testsuite/gcc.dg/field-merge-9.c
> index 25a8b1fa9b0ab..b9e08d8fa37d2 100644
> --- a/gcc/testsuite/gcc.dg/field-merge-9.c
> +++ b/gcc/testsuite/gcc.dg/field-merge-9.c
> @@ -1,5 +1,5 @@
> /* { dg-do run } */
> -/* { dg-options "-O" } */
> +/* { dg-options "-O -fdump-tree-ifcombine-details" } */
>
> /* Check that conversions and selections of similar bit ranges across
> different
> types don't prevent combination. */
> @@ -25,7 +25,7 @@ void f (void) {
> if (0
> || p.a != q.a
> || p.b[!le] != (unsigned char)q.b
> - || p.b[le] != (char)((q.b >> (__CHAR_BIT__ - 1)) >> 1)
> + || p.b[le] != (unsigned char)((q.b >> (__CHAR_BIT__ - 1)) >> 1)
> )
> __builtin_abort ();
> }
> @@ -34,3 +34,5 @@ int main () {
> f ();
> return 0;
> }
> +
> +/* { dg-final { scan-tree-dump-times "optimizing two comparisons" 2
> "ifcombine" } } */
>
>
> --
> Alexandre Oliva, happy hacker https://FSFLA.org/blogs/lxo/
> Free Software Activist GNU Toolchain Engineer
> More tolerance and less prejudice are key for inclusion and diversity
> Excluding neuro-others for not behaving ""normal"" is *not* inclusive