Hi Jeff and Andrea,
Now that Eikash's patch for PR tree-opt/125385 is in the tree,
I've updated my (float)i == 4.0f patch against the current mainline.
The full technical description is in my post from July 2024:
https://gcc.gnu.org/pipermail/gcc-patches/2024-July/658482.html
which was initially (positively) reviewed here:
https://gcc.gnu.org/pipermail/gcc-patches/2024-August/659433.html
and my recent ping (which contained some more context) is at
https://gcc.gnu.org/pipermail/gcc-patches/2026-June/720287.html
Eikash's patch/optimization is cool, and obsoletes all the ranger
related pieces of my original patch. Thanks Eikash and Andrea.
This patch has been retested on x86_64-pc-linux-gnu with make bootstrap
and make -k check, both with and without --target_board=unix{-m32}
with no new failures. Ok for mainline?
2026-07-10 Roger Sayle <[email protected]>
gcc/ChangeLog
PR tree-optimization/57371
* fold-const.cc (fold_cmp_float_cst_p): New helper function.
* fold-const.h (fold_cmp_float_cst_p): Prototype here.
* match.pd ((FTYPE) N CMP CST): Use the new helper
fold_cmp_float_cst_p to check that transformation to an integer
comparison is safe.
gcc/testsuite/ChangeLog
PR tree-optimization/57371
* c-c++-common/pr57371-6.c: New test case.
* c-c++-common/pr57371-7.c: Likewise.
* c-c++-common/pr57371-8.c: Likewise.
* c-c++-common/pr57371-9.c: Likewise.
* c-c++-common/pr57371-10.c: Likewise.
Thanks again,
Roger
--
> -----Original Message-----
> From: Andrea Pinski <[email protected]>
> Sent: 19 June 2026 07:32
> To: Andrew Pinski <[email protected]>
> Cc: Roger Sayle <[email protected]>; GCC Patches <gcc-
> [email protected]>; Jeffrey Law <[email protected]>
> Subject: Re: [PATCH] PR tree-opt/57371: Improved comparison of integers to FP
> constants.
>
> On Mon, Jun 15, 2026 at 1:21 AM Andrew Pinski <[email protected]> wrote:
> >
> >
> >
> > On Mon, Jun 15, 2026, 1:09 AM Roger Sayle <[email protected]>
> wrote:
> >>
> >>
> >> Hi Jeff,
> >> I'd like to ping/progress a patch of mine from July 2024:
> >> https://gcc.gnu.org/pipermail/gcc-patches/2024-July/658482.html
> >> which was initially (positively) reviewed here:
> >> https://gcc.gnu.org/pipermail/gcc-patches/2024-August/659433.html
> >>
> >> The original motivation for this patch was Claudiu's issue/analysis
> >> at
> >> https://github.com/foss-for-synopsys-dwc-arc-processors/gcc/issues/11
> >> 8 which predicted a dramatic +26% performance improvement on EEMBC's
> >> iirflt01 benchmark.
> >>
> >> A quick summary is that GCC currently fails to optimize:
> >> int foo(long long x) { return x == 1.0; } to use an integral
> >> comparison (and avoid a floating point conversion) because all the
> >> values of long long can't be precisely represented as a (IEEE)
> >> double. This is too strict a condition, as only one value of a "long
> >> long" gets converted to 1.0 (even with overflow and rounding).
> >>
> >> The code review above agreed the implementation itself is reasonable
> >> and both machine and floating-point independent, the challenge is in
> >> writing test cases for the code, which unfortunately must assume a
> >> particular floating point format, and are therefore non-portable.
> >>
> >> To respond to Jeff's previous request/question, my preferred solution
> >> is rather than try to add "-mieee" to these new test cases, which
> >> should still pass even without strict IEEE (i.e. NaN) support, and
> >> therefore we should still be testing with -O2 default flags, but
> >> instead it makes sense for various backends to add additional
> >> tests/copies to their gcc.target directories including "-mieee",
> >> "-msoft-float", "-md-float", "-mg-float", "-mfpmath=387" etc. if they
> >> so wish. A failure of these tests does not indicate an issue with
> >> the implementation, which simply uses the current compile-time
> >> integer to FP (real) conversion support, but that that test hasn't
> >> been ported to the new/native floating point format (say FP8 or FP4).
> >>
> >> This patch has been retested on x86_64-pc-linux-gnu with make
> >> bootstrap and make -k check, both with and without
> >> --target_board=unix{-m32} with no new failures. Ok for mainline
> >> (cleaning up any testsuite wrinkles if/as they're reported)?
> >>
> >>
> >> 2026-06-15 Roger Sayle <[email protected]>
> >>
> >> gcc/ChangeLog
> >> PR tree-optimization/57371
> >> * fold-const.cc (fold_cmp_float_cst_p): New helper function.
> >> * fold-const.h (fold_cmp_float_cst_p): Prototype here.
> >> * match.pd ((FTYPE) N CMP CST): Use ranger to determine
> >> whether value is exactly representable by floating point type,
> >> and check flag_trapping_math if not. Use the new helper
> >> fold_cmp_float_cst_p to check that transformation to an integer
> >> comparison is safe.
> >
> >
> > Hmm, I have someone working on a related patch and this will conflict.
> > We are changing (cmp (convert int) (float_cst)) to use the ranger and
> > adding a
> new function to the format helper to check if the range of the integer can
> fit into
> the floating point exactly.
> > The person working on this should be posting today or tomorrow.
> > Since I helped with that patch I can review this one too.
>
> See https://gcc.gnu.org/pipermail/gcc-patches/2026-June/720633.html .
> I do know there is some overlap between the 2 patches.
> Like for an example in your patch you do:
> ```
>
> + if (!vr0.undefined_p ())
> + {
> + lo = vr0.lower_bound ();
> + hi = vr0.upper_bound ();
> + int prec = (isign == SIGNED)
> + ? MAX (wi::min_precision (hi, SIGNED),
> + wi::min_precision (lo, SIGNED)) - 1
> + : wi::min_precision (hi, UNSIGNED);
> + wide_int nz = vr0.get_nonzero_bits ();
> + if (nz != 0)
> + prec -= wi::ctz (nz);
> + exact_p = prec <= significand_size (fmt); }
> ``
>
> While Eikansh does:
>
> +#if GIMPLE
> + int_range_max vr;
> + if (!value_ok
> + && gimple_match_range_of_expr (vr, @0, @2)) value_ok =
> +fmt.can_represent_integral_value_p (&vr); #endif
>
> And then can_represent_integral_value_p calls range_fits_type_p which is
> basically what you do here (but better):
> + int prec = (isign == SIGNED)
> + ? MAX (wi::min_precision (hi, SIGNED),
> + wi::min_precision (lo, SIGNED)) - 1
> + : wi::min_precision (hi, UNSIGNED);
> + wide_int nz = vr0.get_nonzero_bits ();
> + if (nz != 0)
> + prec -= wi::ctz (nz);
> + exact_p = prec <= significand_size (fmt);
>
>
>
>
> >
> > Thanks,
> > Andrea
> >
> >
> >>
> >> gcc/testsuite/ChangeLog
> >> PR tree-optimization/57371
> >> * c-c++-common/pr57371-6.c: New test case.
> >> * c-c++-common/pr57371-7.c: Likewise.
> >> * c-c++-common/pr57371-8.c: Likewise.
> >> * c-c++-common/pr57371-9.c: Likewise.
> >> * c-c++-common/pr57371-10.c: Likewise.
> >>
> >>
> >> Thanks in advance,
> >> Roger
> >> --
> >>
diff --git a/gcc/rtl.cc b/gcc/rtl.cc
index 3839c364571..dac230b2159 100644
--- a/gcc/rtl.cc
+++ b/gcc/rtl.cc
@@ -594,12 +594,13 @@ rtvec_all_equal_p (const_rtvec vec)
{ START, START+1, START+2, ... }. */
bool
-rtvec_series_p (rtvec vec, int start)
+rtvec_series_p (rtvec vec, poly_int64 start)
{
for (int i = 0; i < GET_NUM_ELEM (vec); i++)
{
- rtx x = RTVEC_ELT (vec, i);
- if (!CONST_INT_P (x) || INTVAL (x) != i + start)
+ poly_int64 elt;
+ if (!poly_int_rtx_p (RTVEC_ELT (vec, i), &elt)
+ || maybe_ne (elt, i + start))
return false;
}
return true;
diff --git a/gcc/rtl.h b/gcc/rtl.h
index 747d917ecf2..55a7548e31f 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -3085,7 +3085,7 @@ extern bool rtx_equal_p (const_rtx, const_rtx,
rtx_equal_p_callback_function = NULL);
extern bool rtvec_all_equal_p (const_rtvec);
-extern bool rtvec_series_p (rtvec, int);
+extern bool rtvec_series_p (rtvec, poly_int64);
/* Return true if X is a vector constant with a duplicated element value. */
diff --git a/gcc/rtlanal.cc b/gcc/rtlanal.cc
index 5274a5c59cf..d897dc400ea 100644
--- a/gcc/rtlanal.cc
+++ b/gcc/rtlanal.cc
@@ -7057,14 +7057,12 @@ register_asm_p (const_rtx x)
bool
vec_series_highpart_p (machine_mode result_mode, machine_mode op_mode, rtx sel)
{
- int nunits;
- if (GET_MODE_NUNITS (op_mode).is_constant (&nunits)
- && targetm.can_change_mode_class (op_mode, result_mode, ALL_REGS))
- {
- int offset = BYTES_BIG_ENDIAN ? 0 : nunits - XVECLEN (sel, 0);
- return rtvec_series_p (XVEC (sel, 0), offset);
- }
- return false;
+ if (!targetm.can_change_mode_class (op_mode, result_mode, ALL_REGS))
+ return false;
+ if (BYTES_BIG_ENDIAN)
+ return rtvec_series_p (XVEC (sel, 0), 0);
+ poly_int64 offset = GET_MODE_NUNITS (op_mode) - XVECLEN (sel, 0);
+ return rtvec_series_p (XVEC (sel, 0), offset);
}
/* Return true if, for all OP of mode OP_MODE:
@@ -7076,14 +7074,12 @@ vec_series_highpart_p (machine_mode result_mode,
machine_mode op_mode, rtx sel)
bool
vec_series_lowpart_p (machine_mode result_mode, machine_mode op_mode, rtx sel)
{
- int nunits;
- if (GET_MODE_NUNITS (op_mode).is_constant (&nunits)
- && targetm.can_change_mode_class (op_mode, result_mode, ALL_REGS))
- {
- int offset = BYTES_BIG_ENDIAN ? nunits - XVECLEN (sel, 0) : 0;
- return rtvec_series_p (XVEC (sel, 0), offset);
- }
- return false;
+ if (!targetm.can_change_mode_class (op_mode, result_mode, ALL_REGS))
+ return false;
+ if (!BYTES_BIG_ENDIAN)
+ return rtvec_series_p (XVEC (sel, 0), 0);
+ poly_int64 offset = GET_MODE_NUNITS (op_mode) - XVECLEN (sel, 0);
+ return rtvec_series_p (XVEC (sel, 0), offset);
}
/* Return true if X contains a paradoxical subreg. */
@@ -7100,3 +7096,76 @@ contains_paradoxical_subreg_p (rtx x)
}
return false;
}
+
+/* Analyze X as accessing a consecutive sequence of bytes in some base
+ rtx that is no smaller than X. Return the base value and set *OFFSET_PTR
+ to the byte offset of X from the start of the base. Like SUBREG_BYTE,
+ this byte offset follows memory order. */
+
+rtx
+get_ref_base_and_offset (rtx x, poly_uint64 *offset_ptr)
+{
+ poly_uint64 outer_bytes = GET_MODE_SIZE (GET_MODE (x));
+ poly_uint64 offset = 0;
+ for (;;)
+ {
+ switch (GET_CODE (x))
+ {
+ case SUBREG:
+ if (!paradoxical_subreg_p (x))
+ {
+ offset += SUBREG_BYTE (x);
+ x = SUBREG_REG (x);
+ continue;
+ }
+ break;
+
+ case ASHIFT:
+ case LSHIFTRT:
+ case ASHIFTRT:
+ if (SCALAR_INT_MODE_P (GET_MODE (x))
+ && CONST_INT_P (XEXP (x, 1))
+ && UINTVAL (XEXP (x, 1)) % BITS_PER_UNIT == 0)
+ {
+ auto inner_bytes = GET_MODE_SIZE (GET_MODE (x));
+ /* Reanalyze the current extraction as a shift right of X. */
+ poly_uint64 lsb = subreg_size_lsb (outer_bytes, inner_bytes,
+ offset);
+ /* Convert it to a shift right of XEXP (x, 0). This might
+ wrap. */
+ if (GET_CODE (x) == ASHIFT)
+ lsb -= UINTVAL (XEXP (x, 1));
+ else
+ lsb += UINTVAL (XEXP (x, 1));
+ if (known_le (lsb, (inner_bytes - outer_bytes) * BITS_PER_UNIT))
+ {
+ x = XEXP (x, 0);
+ offset = subreg_size_offset_from_lsb (outer_bytes,
+ inner_bytes, lsb);
+ continue;
+ }
+ }
+ break;
+
+ case VEC_SELECT:
+ {
+ rtx sel = XEXP (x, 1);
+ poly_int64 start;
+ if (poly_int_rtx_p (XVECEXP (sel, 0, 0), &start)
+ && rtvec_series_p (XVEC (sel, 0), start))
+ {
+ x = XEXP (x, 0);
+ offset += start * GET_MODE_UNIT_SIZE (GET_MODE (x));
+ continue;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ *offset_ptr = offset;
+ return x;
+ }
+}
diff --git a/gcc/rtlanal.h b/gcc/rtlanal.h
index 04c3b1b9a59..99addaa80ff 100644
--- a/gcc/rtlanal.h
+++ b/gcc/rtlanal.h
@@ -336,8 +336,12 @@ vec_series_highpart_p (machine_mode result_mode,
machine_mode op_mode,
rtx sel);
bool
-vec_series_lowpart_p (machine_mode result_mode, machine_mode op_mode, rtx sel);
+vec_series_lowpart_p (machine_mode result_mode, machine_mode op_mode,
+ rtx sel);
bool
contains_paradoxical_subreg_p (rtx x);
+
+rtx
+get_ref_base_and_offset (rtx x, poly_uint64 *offset_ptr);
#endif
diff --git a/gcc/simplify-rtx.cc b/gcc/simplify-rtx.cc
index 92a2a6e954a..32267ac061b 100644
--- a/gcc/simplify-rtx.cc
+++ b/gcc/simplify-rtx.cc
@@ -5586,17 +5586,30 @@ simplify_ashift:
return simplify_gen_binary (VEC_SELECT, mode, XEXP (trueop0, 0),
gen_rtx_PARALLEL (VOIDmode, vec));
}
- /* (vec_concat:
- (subreg_lowpart:N OP)
- (vec_select:N OP P)) --> OP when P selects the high half
- of the OP. */
- if (GET_CODE (trueop0) == SUBREG
- && subreg_lowpart_p (trueop0)
- && GET_CODE (trueop1) == VEC_SELECT
- && SUBREG_REG (trueop0) == XEXP (trueop1, 0)
- && !side_effects_p (XEXP (trueop1, 0))
- && vec_series_highpart_p (op1_mode, mode, XEXP (trueop1, 1)))
- return XEXP (trueop1, 0);
+ /* (vec_concat:N
+ (subreg:N/2 OP 0)
+ (subreg:N/2 OP N/2)) --> OP
+ i.e. where concatenating the first and second halves of the
+ same object OP. */
+ if (known_eq (GET_MODE_SIZE (op0_mode),
+ GET_MODE_SIZE (op1_mode)))
+ {
+ poly_uint64 offset = 0u;
+ rtx base0 = get_ref_base_and_offset (trueop0, &offset);
+ if (base0
+ && known_eq (offset, 0u)
+ && known_eq (GET_MODE_SIZE (GET_MODE (base0)),
+ GET_MODE_SIZE (mode)))
+ {
+ rtx base1 = get_ref_base_and_offset (trueop1, &offset);
+ if (base1
+ && rtx_equal_p (base0, base1)
+ && known_eq (offset, GET_MODE_SIZE (op0_mode))
+ && !side_effects_p (trueop0)
+ && !side_effects_p (trueop1))
+ return gen_lowpart (mode, base0);
+ }
+ }
}
return 0;
diff --git a/gcc/testsuite/gcc.target/i386/pr48609.c
b/gcc/testsuite/gcc.target/i386/pr48609.c
new file mode 100644
index 00000000000..8af9d883761
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr48609.c
@@ -0,0 +1,13 @@
+/* PR target/48609 */
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -msse2" } */
+typedef _Complex float SCtype;
+extern SCtype bar;
+void foo (SCtype x)
+{
+ bar = x;
+}
+
+/* { dg-final { scan-assembler-not "movdqa" } } */
+/* { dg-final { scan-assembler-not "shufps" } } */
+/* { dg-final { scan-assembler-not "unpcklps" } } */