On Thu, Dec 5, 2024 at 1:39 PM Alexandre Oliva <ol...@adacore.com> 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