[PATCH] fold-const, match.pd: Improve ptr_difference_const optimizations

Jakub Jelinek <[email protected]> Tue, 4 Aug 2026 21:56:44 +0200
Newsgroups gmane.comp.gcc.patches
Message-ID <anJD_Hxe4upyTp6c@tucnak>
Hi!

As mentioned in https://gcc.gnu.org/pipermail/gcc-patches/2026-July/725438.html
at least the
      /* Convert (T1)(X p+ Y) into ((T1)X p+ Y), for pointer type, when the new
         cast (T1)X will fold away.  We assume that this happens when X itself
         is a cast.  */
fold-const.cc (fold_unary) optimization is highly undesirable in the C++ FE
(at least before the cp_fold_function/gimplification).  In the above patch
I've tried to disable some optimizations (and Jason suggested to disable
them even slightly less), but still that can at least in theory (but with
the above patch in practice too) result in regressions, in particular in
POINTER_DIFF_EXPR constant evaluation.
In that testcase, we end up with
(((((int*)(& x)) + 12) - (((int*)(& x)) + 4)) / 4)
and the current ptr_difference_const match.pd optimizations aren't
prepared to handle that.

The following patch fixes that by adding STRIP_NOPS (exp); to
split_address_to_core_and_offset which ptr_difference_const uses
and then just throwing some ADDR_EXPR requirements in the simplification
patterns - ptr_difference_const will return true if it is something it can
handle, and it doesn't have to be just ADDR_EXPR, it can be also
POINTER_PLUS_EXPR with constant offset, or nop conversions around either of those
or nop conversions around first POINTER_PLUS_EXPR operand.

In some cases after dropping the ADDR_EXPR parts I could throw away one
pattern because two adjusted patterns were identical, in one case (minus
with casts meant for subtraction of pointers cast to intptr_t/uintptr_t)
I had to add another tree_nop_conversion_p check, so that e.g. floating
point difference x - x which can't be folded away because it could trap
isn't attempted to be folded (as build_int_cst for floating point would
ICE).

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

And/or should I try even harder, handle not just SSA_NAME with ADDR_EXPR
as def_stmt, but also with nop conversions or POINTER_PLUS, and in case
of pointer plus perhaps even recurse and add the two bitpos/offsets
together?

2026-08-04  Jakub Jelinek  <[email protected]>

	* fold-const.cc (split_address_to_core_and_offset): Use STRIP_NOPS.
	* match.pd ((&a + b) !=/== (&a[1] + c) -> (&a[0] - &a[1]) + b !=/== c):
	Don't require captures to be ADDR_EXPR.
	((&a+b) - (&a[1] + c) -> sizeof(a[0]) + (b - c)): Likewise.
	((p + b) - &p->d -> offsetof (*p, d) + b): Likewise.
	(Try folding difference of addresses): Likewise.  Remove
	redundant simplifications because of that.

--- gcc/fold-const.cc.jj	2026-07-26 15:37:03.625407289 +0200
+++ gcc/fold-const.cc	2026-08-04 16:06:17.012862597 +0200
@@ -16166,6 +16166,8 @@ split_address_to_core_and_offset (tree e
   poly_int64 bitsize;
   location_t loc = EXPR_LOCATION (exp);
 
+  STRIP_NOPS (exp);
+
   if (TREE_CODE (exp) == SSA_NAME)
     if (gassign *def = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (exp)))
       if (gimple_assign_rhs_code (def) == ADDR_EXPR)
--- gcc/match.pd.jj	2026-08-04 10:27:31.000000000 +0200
+++ gcc/match.pd	2026-08-04 18:14:02.108482282 +0200
@@ -3089,12 +3089,12 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 /* (&a + b) !=/== (&a[1] + c) -> (&a[0] - &a[1]) + b !=/== c */
 (for neeq (ne eq)
  (simplify
-  (neeq:c (pointer_plus @2 @3) ADDR_EXPR@0)
+  (neeq:c (pointer_plus @2 @3) @0)
    (with { poly_int64 diff; tree inner_type = TREE_TYPE (@3);}
     (if (ptr_difference_const (@0, @2, &diff))
      (neeq { build_int_cst_type (inner_type, diff); } @3))))
  (simplify
-  (neeq (pointer_plus ADDR_EXPR@0 @1) (pointer_plus ADDR_EXPR@2 @3))
+  (neeq (pointer_plus @0 @1) (pointer_plus @2 @3))
    (with { poly_int64 diff; tree inner_type = TREE_TYPE (@1);}
     (if (ptr_difference_const (@0, @2, &diff))
      (neeq (plus { build_int_cst_type (inner_type, diff); } @1) @3)))))
@@ -3310,38 +3310,29 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 
 /* Try folding difference of addresses.  */
 (simplify
- (minus (convert ADDR_EXPR@0) (convert (pointer_plus @1 @2)))
- (if (tree_nop_conversion_p (type, TREE_TYPE (@0)))
+ (minus (convert @0) (convert (pointer_plus @1 @2)))
+ (if (tree_nop_conversion_p (type, TREE_TYPE (@0))
+      && tree_nop_conversion_p (type, TREE_TYPE (@1)))
   (with { poly_int64 diff; }
    (if (ptr_difference_const (@0, @1, &diff))
     (minus { build_int_cst_type (type, diff); } (convert @2))))))
 (simplify
- (minus (convert (pointer_plus @0 @2)) (convert ADDR_EXPR@1))
- (if (tree_nop_conversion_p (type, TREE_TYPE (@0)))
+ (minus (convert (pointer_plus @0 @2)) (convert @1))
+ (if (tree_nop_conversion_p (type, TREE_TYPE (@0))
+      && tree_nop_conversion_p (type, TREE_TYPE (@1)))
   (with { poly_int64 diff; }
    (if (ptr_difference_const (@0, @1, &diff))
     (plus (convert @2) { build_int_cst_type (type, diff); })))))
 (simplify
- (minus (convert ADDR_EXPR@0) (convert @1))
- (if (tree_nop_conversion_p (type, TREE_TYPE (@0)))
+ (minus (convert @0) (convert @1))
+ (if (tree_nop_conversion_p (type, TREE_TYPE (@0))
+      && tree_nop_conversion_p (type, TREE_TYPE (@1))
+      && tree_nop_conversion_p (type, ptr_type_node))
   (with { poly_int64 diff; }
    (if (ptr_difference_const (@0, @1, &diff))
     { build_int_cst_type (type, diff); }))))
 (simplify
- (minus (convert @0) (convert ADDR_EXPR@1))
- (if (tree_nop_conversion_p (type, TREE_TYPE (@0)))
-  (with { poly_int64 diff; }
-   (if (ptr_difference_const (@0, @1, &diff))
-    { build_int_cst_type (type, diff); }))))
-(simplify
- (pointer_diff (convert?@2 ADDR_EXPR@0) (convert1?@3 @1))
- (if (tree_nop_conversion_p (TREE_TYPE(@2), TREE_TYPE (@0))
-      && tree_nop_conversion_p (TREE_TYPE(@3), TREE_TYPE (@1)))
-  (with { poly_int64 diff; }
-   (if (ptr_difference_const (@0, @1, &diff))
-    { build_int_cst_type (type, diff); }))))
-(simplify
- (pointer_diff (convert?@2 @0) (convert1?@3 ADDR_EXPR@1))
+ (pointer_diff (convert?@2 @0) (convert1?@3 @1))
  (if (tree_nop_conversion_p (TREE_TYPE(@2), TREE_TYPE (@0))
       && tree_nop_conversion_p (TREE_TYPE(@3), TREE_TYPE (@1)))
   (with { poly_int64 diff; }
@@ -3350,18 +3341,18 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 
 /* (&a+b) - (&a[1] + c) -> sizeof(a[0]) + (b - c) */
 (simplify
- (pointer_diff (pointer_plus ADDR_EXPR@0 @1) (pointer_plus ADDR_EXPR@2 @3))
+ (pointer_diff (pointer_plus @0 @1) (pointer_plus @2 @3))
  (with { poly_int64 diff; }
    (if (ptr_difference_const (@0, @2, &diff))
     (plus { build_int_cst_type (type, diff); } (convert (minus @1 @3))))))
 /* (p + b) - &p->d -> offsetof (*p, d) + b */
 (simplify
- (pointer_diff (pointer_plus @0 @1) ADDR_EXPR@2)
+ (pointer_diff (pointer_plus @0 @1) @2)
  (with { poly_int64 diff; }
    (if (ptr_difference_const (@0, @2, &diff))
     (plus { build_int_cst_type (type, diff); } (convert @1)))))
 (simplify
- (pointer_diff ADDR_EXPR@0 (pointer_plus @1 @2))
+ (pointer_diff @0 (pointer_plus @1 @2))
  (with { poly_int64 diff; }
    (if (ptr_difference_const (@0, @1, &diff))
     (minus { build_int_cst_type (type, diff); } (convert @2)))))

	Jakub