Hi,

This is a regression present on the mainline, 16 and 15 branches.  The problem 
is that the DSE3 pass incorrectly deletes a store to an up-level reference to 
an array with negative low bound:

Deleted dead store: FRAME.19.s[0]{lb: 18446744073709551613 sz: 8} = 0.0;

because IPA modref incorrectly flags it as dead for this call:

ipa-modref: call stmt opt110.term (1, 0, 
2.37000000000000010658141036401502788066864013671875e+0, 
2.79323000000000001818989403545856475830078125e+3); [static-chain: &FRAME.19]
ipa-modref: call to Opt110.Term/3 does not use ref: FRAME.19.s[0]{lb: 
18446744073709551613 sz: 8} alias sets: 13->11

The array at stake is declared with a negative low bound:

N : constant := 3;

S : array (-N..0) of REAL;


The origin of the problem is visible when you compare the modref1 and modref2 
dump files:

 - Analyzing load: CHAIN.20_21(D)->s[_7]{lb: 18446744073709551613 sz: 8}
 (can throw; marking side effects)    - Recording base_set=0 ref_set=0  Static 
chain param offset:0 offset:960 size:64 max_size:256

vs

 - Analyzing load: CHAIN.20_12(D)->s[_4]{lb: 18446744073709551613 sz: 8}
 (can throw; marking side effects)    - Recording base_set=0 ref_set=0  Static 
chain param offset:0 offset:960 size:64 max_size:192

The max_size has been incorrectly shrunk from 256 to 192 bits, the missing 64 
bits being the FRAME.19.s[0]{lb: 18446744073709551613 sz: 8} element.


The problematic code is in get_ref_base_and_extent:

        if (TREE_CODE (index) == SSA_NAME
            && (low_bound = array_ref_low_bound (exp),
                poly_int_tree_p (low_bound))
            && (unit_size = array_ref_element_size (exp),
                TREE_CODE (unit_size) == INTEGER_CST)
            && query->range_of_expr (vr, index)
            && !vr.varying_p ()
            && !vr.undefined_p ())
          {
            wide_int min = vr.lower_bound ();
            wide_int max = vr.upper_bound ();
            poly_offset_int lbound = wi::to_poly_offset (low_bound);
            /* Try to constrain maxsize with range information.  */

It uses the range of the index expression to constrain the maxsize:

[irange] sizetype [0, 0][18446744073709551613, +INF]

but that's not correct in the general case, the correct range being instead 
the range of the difference between the low bound and the index expression,
as implemented by the model computation in get_inner_reference:

[irange] sizetype [0, 3]

Of course this makes no difference for the C family of languages where the low 
bound is always 0.  There is also the problematic case where this low bound is 
a poly_int instead of an integer, given that ranges do not support poly_ints, 
but I'm not sure whether that's a practical concern; that's why I'm proposing 
two versions of the fix, one filtering it out and the other keeping it.

Bootstrapped/regtested on x86-64/Linux, OK for the mainline as well as the 16 
and 15 branches?  If so, which version?


2026-08-24  Eric Botcazou  <[email protected]>

        PR ada/125984
        * tree-dfa.cc (get_ref_base_and_extent) <ARRAY_REF>: Use the range
        of the difference between the index expression and the low bound,
        instead of that of the index expression itself, to constrain the
        maximum size of the access.


2026-08-24  Eric Botcazou  <[email protected]>

        * gnat.dg/opt110.adb: New test.

-- 
Eric Botcazou
diff --git a/gcc/tree-dfa.cc b/gcc/tree-dfa.cc
index 5ba5f10ecb6..4c6f29155b0 100644
--- a/gcc/tree-dfa.cc
+++ b/gcc/tree-dfa.cc
@@ -530,30 +530,40 @@ get_ref_base_and_extent (tree exp, poly_int64 *poffset,
 		   index.  */
 		seen_variable_array_ref = true;
 
+		/* Try to constrain the access range by using the range of the
+		   index expression when it is known. Extra care must be taken
+		   when the low bound of the array is not zero, because it may
+		   be very large and the index expression may be unsigned and
+		   wrap around; in this case, the correct range is that of the
+		   difference between the index expression and the low bound,
+		   see get_inner_reference for the model computation.  */
+		range_query *query = get_range_query (cfun);
 		int_range_max vr;
-		range_query *query;
-		query = get_range_query (cfun);
 
 		if (TREE_CODE (index) == SSA_NAME
 		    && (low_bound = array_ref_low_bound (exp),
-			poly_int_tree_p (low_bound))
+			TREE_CODE (low_bound) == INTEGER_CST)
 		    && (unit_size = array_ref_element_size (exp),
 			TREE_CODE (unit_size) == INTEGER_CST)
 		    && query->range_of_expr (vr, index)
 		    && !vr.varying_p ()
-		    && !vr.undefined_p ())
+		    && !vr.undefined_p ()
+		    && (integer_zerop (low_bound)
+			|| query->range_of_expr
+			     (vr,
+			      build2 (MINUS_EXPR, TREE_TYPE (index),
+				      index, low_bound))))
 		  {
 		    wide_int min = vr.lower_bound ();
 		    wide_int max = vr.upper_bound ();
-		    poly_offset_int lbound = wi::to_poly_offset (low_bound);
 		    /* Try to constrain maxsize with range information.  */
 		    offset_int omax
 		      = offset_int::from (max, TYPE_SIGN (TREE_TYPE (index)));
 		    if (wi::get_precision (max) <= ADDR_MAX_BITSIZE
-			&& known_lt (lbound, omax))
+			&& omax >= 0)
 		      {
-			poly_offset_int rmaxsize;
-			rmaxsize = (omax - lbound + 1)
+			offset_int rmaxsize
+			  = (omax + 1)
 			    * wi::to_offset (unit_size) << LOG2_BITS_PER_UNIT;
 			if (!known_size_p (maxsize)
 			    || known_lt (rmaxsize, maxsize))
@@ -569,11 +579,10 @@ get_ref_base_and_extent (tree exp, poly_int64 *poffset,
 		    offset_int omin
 		      = offset_int::from (min, TYPE_SIGN (TREE_TYPE (index)));
 		    if (wi::get_precision (min) <= ADDR_MAX_BITSIZE
-			&& known_le (lbound, omin))
+			&& omin > 0)
 		      {
-			poly_offset_int woffset
-			  = wi::sext (omin - lbound,
-				      TYPE_PRECISION (sizetype));
+			offset_int woffset
+			  = wi::sext (omin, TYPE_PRECISION (sizetype));
 			woffset *= wi::to_offset (unit_size);
 			woffset <<= LOG2_BITS_PER_UNIT;
 			bit_offset += woffset;
diff --git a/gcc/tree-dfa.cc b/gcc/tree-dfa.cc
index 5ba5f10ecb6..87017c6b44f 100644
--- a/gcc/tree-dfa.cc
+++ b/gcc/tree-dfa.cc
@@ -530,9 +530,15 @@ get_ref_base_and_extent (tree exp, poly_int64 *poffset,
 		   index.  */
 		seen_variable_array_ref = true;
 
+		/* Try to constrain the access range by using the range of the
+		   index expression when it is known. Extra care must be taken
+		   when the low bound of the array is a nonzero constant, as it
+		   may be very large and the index expression be unsigned and
+		   wrap around; in this case, the correct range is that of the
+		   difference between the index expression and the low bound,
+		   see get_inner_reference for the model computation.  */
+		range_query *query = get_range_query (cfun);
 		int_range_max vr;
-		range_query *query;
-		query = get_range_query (cfun);
 
 		if (TREE_CODE (index) == SSA_NAME
 		    && (low_bound = array_ref_low_bound (exp),
@@ -541,16 +547,26 @@ get_ref_base_and_extent (tree exp, poly_int64 *poffset,
 			TREE_CODE (unit_size) == INTEGER_CST)
 		    && query->range_of_expr (vr, index)
 		    && !vr.varying_p ()
-		    && !vr.undefined_p ())
+		    && !vr.undefined_p ()
+		    && (POLY_INT_CST_P (low_bound)
+			|| integer_zerop (low_bound)
+			|| (query->range_of_expr
+			      (vr,
+			       build2 (MINUS_EXPR, TREE_TYPE (index),
+				       index, low_bound)))))
 		  {
 		    wide_int min = vr.lower_bound ();
 		    wide_int max = vr.upper_bound ();
-		    poly_offset_int lbound = wi::to_poly_offset (low_bound);
+		    poly_offset_int lbound;
+		    if (POLY_INT_CST_P (low_bound))
+		      lbound = wi::to_poly_offset (low_bound);
+		    else
+		      lbound = 0;
 		    /* Try to constrain maxsize with range information.  */
 		    offset_int omax
 		      = offset_int::from (max, TYPE_SIGN (TREE_TYPE (index)));
 		    if (wi::get_precision (max) <= ADDR_MAX_BITSIZE
-			&& known_lt (lbound, omax))
+			&& known_le (lbound, omax))
 		      {
 			poly_offset_int rmaxsize;
 			rmaxsize = (omax - lbound + 1)
@@ -569,7 +585,7 @@ get_ref_base_and_extent (tree exp, poly_int64 *poffset,
 		    offset_int omin
 		      = offset_int::from (min, TYPE_SIGN (TREE_TYPE (index)));
 		    if (wi::get_precision (min) <= ADDR_MAX_BITSIZE
-			&& known_le (lbound, omin))
+			&& known_lt (lbound, omin))
 		      {
 			poly_offset_int woffset
 			  = wi::sext (omin - lbound,
-- { dg-do run }
-- { dg-options "-O" }

procedure Opt110 is

  type REAL is new Long_Float;

  R : REAL;

  N : constant := 3;

  C2, S2 : array ( 0..N) of REAL;
  C, S   : array (-N..0) of REAL;

  U, V : REAL;

  procedure ADD (C1, S1, C2L, S2L :     REAL;
                 CO, SO           : out REAL) is
  begin
    CO := C1*C2L - S1*S2L;
    SO := S1*C2L + C1*S2L;
  end ADD;

  procedure TERM (I1, I    : Integer;
                  DLC, DLS : REAL) is
  begin
    ADD (C2(I1), S2(I1), C(I), S(I), U, V);
    R := R + DLC*U + DLS*V;
  end TERM;

  procedure PERT_1 is
    COS_M : constant REAL := -0.810937203096633;
    SIN_M : constant REAL := 0.585133192216790;
  begin
    C(-1) := COS_M;
    S(-1) := -SIN_M;
    ADD (C(-1), S(-1), C(-1), S(-1), C(-2), S(-2));
    TERM (1, -1, 0.00,  0.00);
    TERM (2, -1, 0.25, -0.09);
  end PERT_1;

  procedure PERT_2 is
    COS_M : constant REAL := -0.977631621135276;
    SIN_M : constant REAL := 0.210324542924530;
  begin
    C(-1) := COS_M;
    S(-1) := -SIN_M;
    for I in reverse -(N-1) .. -1 loop
      ADD (C(I), S(I), C(-1), S(-1), C(I-1), S(I-1));
    end loop;
    TERM (1, 0, 2.37, 2793.23);
  end PERT_2;

  procedure PERT_3 is
    COS_M : constant REAL := 0.682917440967473;
    SIN_M : constant REAL := 0.730495563862258;
  begin
    C(-1) := COS_M;
    S(-1) := -SIN_M;
    for I in reverse -2 .. -1 loop
      ADD (C(I), S(I), C(-1), S(-1), C(I-1), S(I-1));
    end loop;
    TERM (1, -3, -0.65, 1.02);
    TERM (2, -2, -0.05, 0.04);
    TERM (2, -3, -0.50, 0.45);
  end PERT_3;

  procedure PERT_4 is
    COS_M : constant REAL := -0.238503621267387;
    SIN_M : constant REAL := 0.971141607924582;
  begin
    C(-1) := COS_M;
    S(-1) := -SIN_M;
    for I in reverse -2 .. -1 loop
      ADD (C(I), S(I), C(-1), S(-1), C(I-1), S(I-1));
    end loop;
    TERM (0, -1, -0.05,  1.56);
    TERM (1, -1, -2.62,  1.40);
    TERM (1, -2, -0.47, -0.08);
    TERM (2, -2, -0.73, -0.51);
    TERM (2, -3, -0.14, -0.10);
    TERM (3, -3, -0.01,  0.04);
  end PERT_4;

  COS_M : constant REAL := 0.476541619435702;
  SIN_M : constant REAL := 0.879151912325508;

begin
  R := 0.0;
  C(0) := 1.0;
  S(0) := 0.0;
  C2(0) := 1.0;
  S2(0) := 0.0;
  C2(1) := COS_M;
  S2(1) := SIN_M;
  for I in 2 .. N loop
    ADD (C2(I-1), S2(I-1), C2(1), S2(1), C2(I), S2(I));
  end loop;
  PERT_1;
  PERT_2;
  PERT_3;
  PERT_4;
  if Integer(R) /= 2452 then
    raise Program_Error;
  end if;
end;

Reply via email to