https://gcc.gnu.org/g:3383c738122861c52c150483b012be25d9b9dbac
commit r17-5256-g3383c738122861c52c150483b012be25d9b9dbac Author: Avinal Kumar <[email protected]> Date: Fri Oct 16 07:36:25 2026 -0600 match.pd: Use wide_int for shift-and-mask folding [PR tree-optimization/120524] The pattern that folds (X >> C1) & C2 and (X << C1) & C2 was limited to types with precision <= HOST_BITS_PER_WIDE_INT because it used unsigned HOST_WIDE_INT for mask and zerobits arithmetic. This prevents the optimization from firing on __int128 and wider types at the GIMPLE level. Convert the pattern to use wide_int and remove the TYPE_PRECISION guard. Replace the mode-mask loop with wi::exact_log2 (newmask + 1) to check whether newmask is (2^n - 1) for a power-of-two n >= BITS_PER_UNIT. Handle the case where newmask is all-ones at full type precision separately, since newmask + 1 overflows to zero in that case. --- Changes from v2: - use operator instead of explicit function calls, wi:: supports operator overloading - split tests into right shift with negative assetion and left shift with positive assertion and regression guard I also found a case for option 2 as discussed in the last patch: __int128 f(__int128 a) { return (a << 8) & ((__int128)0xff00); } Without the patch (128-bit, pattern skipped): keeps & 65280 (0xff00) With the patch: widens to & 65535 (0xffff - 16-bit mode mask) this was not required if going for option one, so not included in the patch. PR tree-optimization/120524 gcc/ChangeLog: * match.pd: Use wide_int for shift-and-mask folding [PR120524] gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/pr120524-1.c: New test. * gcc.dg/tree-ssa/pr120524-2.c: New test. Signed-off-by: Avinal Kumar <[email protected]> Diff: --- gcc/match.pd | 51 +++++++++++++++--------------- gcc/testsuite/gcc.dg/tree-ssa/pr120524-1.c | 18 +++++++++++ gcc/testsuite/gcc.dg/tree-ssa/pr120524-2.c | 15 +++++++++ 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/gcc/match.pd b/gcc/match.pd index 1cb710974f7e..a28d307c819c 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -5859,20 +5859,19 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (bit_and (convert?:s@4 (shift:s@5 (convert1?@3 @0) INTEGER_CST@1)) INTEGER_CST@2) (if (tree_nop_conversion_p (TREE_TYPE (@4), TREE_TYPE (@5)) - && TYPE_PRECISION (type) <= HOST_BITS_PER_WIDE_INT && tree_fits_uhwi_p (@1) && tree_to_uhwi (@1) > 0 && tree_to_uhwi (@1) < TYPE_PRECISION (type)) (with { unsigned int shiftc = tree_to_uhwi (@1); - unsigned HOST_WIDE_INT mask = TREE_INT_CST_LOW (@2); - unsigned HOST_WIDE_INT newmask, zerobits = 0; + wide_int mask = wi::to_wide (@2); + wide_int newmask, zerobits = wi::zero (TYPE_PRECISION (type)); tree shift_type = TREE_TYPE (@3); unsigned int prec; if (shift == LSHIFT_EXPR) - zerobits = ((HOST_WIDE_INT_1U << shiftc) - 1); + zerobits = wi::mask (shiftc, false, TYPE_PRECISION (type)); else if (shift == RSHIFT_EXPR && type_has_mode_precision_p (shift_type)) { @@ -5896,12 +5895,9 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) is all ones. */ } } - zerobits = HOST_WIDE_INT_M1U; + zerobits = wi::minus_one (TYPE_PRECISION (type)); if (shiftc < prec) - { - zerobits >>= HOST_BITS_PER_WIDE_INT - shiftc; - zerobits <<= prec - shiftc; - } + zerobits = wi::mask (prec - shiftc, true, TYPE_PRECISION (type)); /* For arithmetic shift if sign bit could be set, zerobits can contain actually sign bits, so no transformation is possible, unless MASK masks them all away. In that @@ -5909,35 +5905,38 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) if (!TYPE_UNSIGNED (TREE_TYPE (@3)) && prec == TYPE_PRECISION (TREE_TYPE (@3))) { - if ((mask & zerobits) == 0) + if ((mask & zerobits) == wi::zero (TYPE_PRECISION (type))) shift_type = unsigned_type_for (TREE_TYPE (@3)); else - zerobits = 0; + zerobits = wi::zero (TYPE_PRECISION (type)); } } } /* ((X << 16) & 0xff00) is (X, 0). */ (if ((mask & zerobits) == mask) { build_int_cst (type, 0); } - (with { newmask = mask | zerobits; } - (if (newmask != mask && (newmask & (newmask + 1)) == 0) - (with - { - /* Only do the transformation if NEWMASK is some integer - mode's mask. */ - for (prec = BITS_PER_UNIT; - prec < HOST_BITS_PER_WIDE_INT; prec <<= 1) - if (newmask == (HOST_WIDE_INT_1U << prec) - 1) - break; - } - (if (prec < HOST_BITS_PER_WIDE_INT - || newmask == HOST_WIDE_INT_M1U) + (with { newmask = (mask | zerobits); + /* Only do the transformation if NEWMASK is some integer mode's + mask, i.e. an all-ones value (2^n - 1) where n is a power of + 2 precision >= BITS_PER_UNIT. We check this by testing + whether newmask + 1 is a power of two via wi::exact_log2. */ + int lg; + /* newmask + 1 overflows at full type precision (e.g. 128-bit + all-ones), so handle that case directly. */ + if (newmask == wi::minus_one (TYPE_PRECISION (type))) + lg = TYPE_PRECISION (type); + else + lg = wi::exact_log2 (newmask + 1); + unsigned int newmask_prec = lg >= 0 ? (unsigned int) lg : 0; } + (if (newmask != mask + && lg > 0 && (newmask_prec & (newmask_prec - 1)) == 0 + && newmask_prec >= BITS_PER_UNIT) (with - { tree newmaskt = build_int_cst_type (TREE_TYPE (@2), newmask); } + { tree newmaskt = wide_int_to_tree (TREE_TYPE (@2), newmask); } (if (!tree_int_cst_equal (newmaskt, @2)) (if (shift_type != TREE_TYPE (@3)) (bit_and (convert (shift:shift_type (convert @3) @1)) { newmaskt; }) - (bit_and @4 { newmaskt; }))))))))))))) + (bit_and @4 { newmaskt; }))))))))))) /* ((1 << n) & M) != 0 -> n == log2 (M) */ (for cmp (ne eq) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr120524-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr120524-1.c new file mode 100644 index 000000000000..24ac98090c27 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr120524-1.c @@ -0,0 +1,18 @@ +/* PR tree-optimization/120524 */ +/* { dg-do compile } */ +/* { dg-require-effective-target int128 } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +/* Verify (a >> 127) & 1 is optimized to (unsigned __int128)a >> 127. + The mask 1 covers all sign-extended bits, so the arithmetic right shift + can be converted to a logical right shift. */ +__int128 f1(__int128 a) { return (a >> 127) & 1; } + +/* Verify (a >> 120) & 0xff is similarly optimized. */ +__int128 f2(__int128 a) { return (a >> 120) & 0xff; } + +/* Arithmetic right shifts should be converted to logical shifts. All BIT_AND + masks should be folded away. */ +/* { dg-final { scan-tree-dump "uint128_t" "optimized" } } */ +/* { dg-final { scan-tree-dump-not "& 1" "optimized" } } */ +/* { dg-final { scan-tree-dump-not "& 255" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr120524-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr120524-2.c new file mode 100644 index 000000000000..5d01607c04c4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr120524-2.c @@ -0,0 +1,15 @@ +/* PR tree-optimization/120524 */ +/* { dg-do compile } */ +/* { dg-require-effective-target int128 } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +/* Left shift: (a << 120) & (0xff <<120) folds to just a << 120. */ +__int128 f1(__int128 a) { return (a << 120) & ((__int128)0xff << 120); } + +/* Verify 64-bit case also works, this is folded by C frontend putting it + here as regression guard. This should fold to (unsigned long)a >> 63. */ +__int128 f2(__int128 a) { return (a >> 63) & 1; } + +/* Verify expected optimized forms: shift with no AND mask. */ +/* { dg-final { scan-tree-dump "<< 120" "optimized" } } */ +/* { dg-final { scan-tree-dump ">> 63" "optimized" } } */
