On 8/24/26 4:00 AM, Richard Biener wrote:
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 ...
Yea, if you don't have the exact name right, I know precisely the
routine pair you're referring to. It takes this kind of think apart to
encourage optimization with nearby code and then tries to put things
back together if no optimization opportunities were found.
It's a painful pair of routines to work with and I wouldn't be keen to
reimplement on gimple.
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?
Rarely, if ever for these cases. I do wonder if we could tackle in
simplify-rtx. It might be painful, but would avoid converting to
BIT_FIELD_REF too early in gimple.
Jeff