On Sun, 23 Aug 2026, Jeffrey Law wrote:

> 
> 
> On 8/14/2026 12:39 AM, Richard Biener wrote:
> > On Thu, 13 Aug 2026, Jeffrey Law wrote:
> >
> >>
> >> On 8/13/2026 1:36 AM, Artemiy Volkov wrote:
> >>> This patch adds a simplification rule for expressions of the form
> >>> ((X >> C1) & C2) << C3, which extract some bits from X at position C1,
> >>> perform an "and" with a mask which is normally just 2^N - 1, then shift
> >>> the result left by C3.  The transformation is limited to cases where X
> >>> is unsigned, has its precision equal to its width and where C1 and C3
> >>> are smaller than the precision of X (the last condition could probably
> >>> be just assumed but I wasn't sure so decided to play it safe).
> >>>
> >>> When all of the above conditions hold, the expression is folded into
> >>> either: (a) (X >> (C1 - C3)) & (C2 << C3) when C1 >= C3, or (b)
> >>> (X << (C3 - C1)) & (C2 << C3) when C1 < C3.  Additional care is required
> >>> to preserve the leading zeros formed by the X >> C1 operation in the
> >>> original expression; to handle this, we clear the leading bits of the
> >>> mask operand as a preliminary step.
> >>>
> >>> The corner case where C1 is one less the precision of X is handled
> >>> elsewhere (and is folded to just (X >> C1) << C3 as long as the LSB of
> >>> C2 isn't 0.)
> >>>
> >>> On aarch64, this results in:
> >>>
> >>>           lsr     x0, x0, 16
> >>>           and     w0, w0, 130816
> >>>
> >>> being emitted instead of:
> >>>
> >>>           lsr     x1, x0, 32
> >>>           lsr     x0, x0, 24
> >>>           ubfiz   w1, w1, 16, 1
> >>>           ubfiz   w0, w0, 8, 8
> >>>           orr     w0, w1, w0
> >>>
> >>> for the expression "((x >> 32) & 1) << 16) | (x >> 24) & 0xff) << 8)".
> >>>
> >>> A couple of new testcases added, with some focus on the case where X is
> >>> shifted too far to the right as described above.
> >>>
> >>> Survives bootstrap and regtest on aarch64-linux-gnu and x86_64-linux-gnu.
> >>>
> >>> gcc/ChangeLog:
> >>>
> >>>           * match.pd: New rule to fold ((X >> C1) & C2) << C3.
> >>>
> >>> gcc/testsuite/ChangeLog:
> >>>
> >>>           * gcc.dg/tree-ssa/match-bit-extract-shift.c: New test.
> >> Interesting you should start looking at this.  I just pointed Daniel at a
> >> closely related problem.
> >>
> >> In particular should we recognize the (x >> C) & 2^n-1 as a BIT_FIELD_REF.
> >> Doing so for the single bit case would help pr32648 on targets that have
> >> single bit extraction/manipulation like RISC-V.
> >>
> >> I vaguely recall concerns that we didn't want to recognize or canonicalize
> >> to
> >> BIT_FIELD_REF in the past, but the details escape me.  Might as well get
> >> that
> >> discussion started since if we target BIT_FIELD_REF it's going to mean this
> >> patch would need further adjustment.  I seem to think it was Richi or
> >> Andrea
> >> that held this position, but far from 100% certain on that.
> > I think we want to avoid multiple ways to express the same thing as
> > that makes writing general simplifiers harder.  ISTR we had patches
> > to lower BIT_FIELD_REFs to shifts and masking to be able to better
> > combine with other operations.  This was for lowering of bitfield
> > component-refs to accessing representatives plus then extracting
> > the accessed bits.
> ACK.  I suspect if we were to steer towards shifts/masks that we'd get
> meaningfully worse code, especially on risc-like targets.  It's hard for
> combine to put all those pieces back together.

Possibly - but I fear we'd need to add sth like combines
make_compound_operation(? the one "expanding" things to be combinable)
to GIMPLE ...

> The main pattern we played with was this which just recognizes a right shift
> followed by a mask with 0x1.  This probably could be handled in gimple->RTL
> expansion reasonably.

Yes.

> > +/* If we mask a right shift with 0x1, then we really just have a single bit
> > +   bitfield extraction.  We could probably any mask 2^n-1 here as a general
> > +   bitfield extraction.  */
> > +(simplify
> > + (bit_and (convert? (rshift SSA_NAME@0 INTEGER_CST@1)) integer_onep
> > + (if (INTEGRAL_TYPE_P (type)
> > +      && tree_int_cst_sgn (@1) >= 0
> > +      && wi::ltu_p (wi::to_wide (@1), TYPE_PRECISION (TREE_TYPE (@0)))
> > +      && (fold_before_rtl_expansion_p ()
> > +          || !flag_tree_vrp
> > +          || optimize_debug)
> > +      && !BYTES_BIG_ENDIAN)
> > +  (convert (BIT_FIELD_REF:boolean_type_node @0
> > +           { build_one_cst (bitsizetype); }
> > +           (convert:bitsizetype @1)))))
> 
> And, no I have no idea why the !flag_tree_vrp is in there... These never got
> to the point of being cleaned up for submission.
>
> There's a secondary class of transformations where we can use a BIT_FIELD_REF
> to elide an outer AND with a constant and convert inner shifts to
> BIT_FIELD_REFs.  For example:
> 
> +/* In this case the masking is applied to the result of a logical
> +   operation.  We can use bitfield extractions and elide the final
> +   mask to a single bit.  */
> +(for logical (bit_and bit_ior bit_xor)
> + (simplify
> +  (bit_and (logical (convert? (rshift SSA_NAME@0 INTEGER_CST@1))
> +                   (convert? (rshift SSA_NAME@2 INTEGER_CST@3)))
> +          integer_onep)
> +  (if (INTEGRAL_TYPE_P (type)
> +       && tree_int_cst_sgn (@1) >= 0
> +       && tree_int_cst_sgn (@3) >= 0
> +       && wi::ltu_p (wi::to_wide (@1), TYPE_PRECISION (TREE_TYPE (@0)))
> +       && wi::ltu_p (wi::to_wide (@3), TYPE_PRECISION (TREE_TYPE (@2)))
> +       && (fold_before_rtl_expansion_p ()
> +           || !flag_tree_vrp
> +           || optimize_debug)
> +       && !BYTES_BIG_ENDIAN)
> +   (logical (convert (BIT_FIELD_REF:boolean_type_node @0
> +                     { build_one_cst (bitsizetype); }
> +                     (convert:bitsizetype @1)))
> +           (convert  (BIT_FIELD_REF:boolean_type_node @2
> +                     { build_one_cst (bitsizetype); }
> +                     (convert:bitsizetype @3)))))))
> 
> That's meaningfully more difficult to capture during expansion and a hell of a
> lot harder to capture during combine.

Hmm, but it looks like a (possibly target specific) ISEL pattern
do apply pre RTL expansion only?  Does the full result pattern
map to an actual instruction?

Richard.

> Jeff
> 
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Abhinav Puri; (HRB 36809, AG Nuernberg)

Reply via email to