The code for statically computing bounds for division and exponent was
incorrect in some cases, possibly leading to wrong results when -gnato2 or
-gnato3 was used. The code for mod and rem was not using optimal bounds, with
possible minor consequences on efficiency of the generated code with -gnato2 or
-gnato3.

Tested on x86_64-pc-linux-gnu, committed on trunk

2012-10-05  Yannick Moy  <m...@adacore.com>

        * checks.adb (Minimize_Eliminate_Overflow_Checks): Correct code
        for the division operation and exponent operation. Adjust bound
        for the mod and rem operations.

Index: checks.adb
===================================================================
--- checks.adb  (revision 192125)
+++ checks.adb  (working copy)
@@ -1209,7 +1209,7 @@
 
       --  Here we know the result is Long_Long_Integer'Base, of that it has
       --  been rewritten because the parent operation is a conversion. See
-      --  Conversion_Optimization.Apply_Arithmetic_Overflow_Checked_Suppressed.
+      --  Apply_Arithmetic_Overflow_Checked_Suppressed.Conversion_Optimization.
 
       else
          pragma Assert
@@ -7087,148 +7087,81 @@
 
             when N_Op_Divide =>
 
-               --  Following seems awfully complex, can it be simplified ???
+               --  If the right operand can only be zero, set 0..0
 
-               Hi := No_Uint;
-               Lo := No_Uint;
+               if Rlo = 0 and then Rhi = 0 then
+                  Lo := Uint_0;
+                  Hi := Uint_0;
 
-               declare
-                  S : Uint;
+               --  Possible bounds of division must come from dividing end
+               --  values of the input ranges (four possibilities), provided
+               --  zero is not included in the possible values of the right
+               --  operand.
 
-               begin
-                  --  First work on finding big absolute result values. These
-                  --  come from dividing large numbers (which we have in Llo
-                  --  and Lhi) by small values, which we need to figure out.
+               --  Otherwise, we just consider two intervals of values for
+               --  the right operand: the interval of negative values (up to
+               --  -1) and the interval of positive values (starting at 1).
+               --  Since division by 1 is the identity, and division by -1
+               --  is negation, we get all possible bounds of division in that
+               --  case by considering:
+               --    - all values from the division of end values of input
+               --      ranges;
+               --    - the end values of the left operand;
+               --    - the negation of the end values of the left operand.
 
-                  --  Case where right operand can be positive
+               else
+                  declare
+                     Mrk : constant Uintp.Save_Mark := Mark;
+                     --  Mark so we can release the RR and Ev values
 
-                  if Rhi > 0 then
+                     Ev1 : Uint;
+                     Ev2 : Uint;
+                     Ev3 : Uint;
+                     Ev4 : Uint;
 
-                     --  Find smallest positive divisor
+                  begin
+                     --  Discard extreme values of zero for the divisor, since
+                     --  they will simply result in an exception in any case.
 
-                     if Rlo > 0 then
-                        S := Rlo;
-                     else
-                        S := Uint_1;
+                     if Rlo = 0 then
+                        Rlo := Uint_1;
+                     elsif Rhi = 0 then
+                        Rhi := -Uint_1;
                      end if;
 
-                     --  Big negative value divided by small positive value
-                     --  generates a candidate for lowest possible result.
+                     --  Compute possible bounds coming from dividing end
+                     --  values of the input ranges.
 
-                     if Llo < 0 then
-                        Min (Lo, Llo / S);
-                     end if;
+                     Ev1 := Llo / Rlo;
+                     Ev2 := Llo / Rhi;
+                     Ev3 := Lhi / Rlo;
+                     Ev4 := Lhi / Rhi;
 
-                     --  Big positive value divided by small positive value
-                     --  generates a candidate for highest possible result.
+                     Lo := UI_Min (UI_Min (Ev1, Ev2), UI_Min (Ev3, Ev4));
+                     Hi := UI_Max (UI_Max (Ev1, Ev2), UI_Max (Ev3, Ev4));
 
-                     if Lhi > 0 then
-                        Max (Hi, Lhi / S);
-                     end if;
-                  end if;
+                     --  If the right operand can be both negative or positive,
+                     --  include the end values of the left operand in the
+                     --  extreme values, as well as their negation.
 
-                  --  Case where right operand can be negative
+                     if Rlo < 0 and then Rhi > 0 then
+                        Ev1 := Llo;
+                        Ev2 := -Llo;
+                        Ev3 := Lhi;
+                        Ev4 := -Lhi;
 
-                  if Rlo < 0 then
-
-                     --  Find smallest absolute value negative divisor
-
-                     if Rhi < 0 then
-                        S := Rhi;
-                     else
-                        S := -Uint_1;
+                        Min (Lo,
+                             UI_Min (UI_Min (Ev1, Ev2), UI_Min (Ev3, Ev4)));
+                        Max (Hi,
+                             UI_Max (UI_Max (Ev1, Ev2), UI_Max (Ev3, Ev4)));
                      end if;
 
-                     --  Big negative value divided by small negative value
-                     --  generates a candidate for largest possible result.
+                     --  Release the RR and Ev values
 
-                     if Llo < 0 then
-                        Max (Hi, Llo / S);
-                     end if;
+                     Release_And_Save (Mrk, Lo, Hi);
+                  end;
+               end if;
 
-                     --  Big positive value divided by small negative value
-                     --  generates a candidate for lowest possible result.
-
-                     if Lhi > 0 then
-                        Min (Lo, Lhi / S);
-                     end if;
-                  end if;
-
-                  --  Now work on finding small absolute result values. These
-                  --  come from dividing small numbers, which we need to figure
-                  --  out, by large values (which we have in Rlo, Rhi).
-
-                  --  Case where left operand can be positive
-
-                  if Lhi > 0 then
-
-                     --  Find smallest positive dividend
-
-                     if Llo > 0 then
-                        S := Llo;
-                     else
-                        S := Uint_1;
-                     end if;
-
-                     --  Small positive values divided by large negative values
-                     --  generate candidates for low results.
-
-                     if Rlo < 0 then
-                        Min (Lo, S / Rlo);
-                     end if;
-
-                     --  Small positive values divided by large positive values
-                     --  generate candidates for high results.
-
-                     if Rhi > 0 then
-                        Max (Hi, S / Rhi);
-                     end if;
-                  end if;
-
-                  --  Case where left operand can be negative
-
-                  if Llo < 0 then
-
-                     --  Find smallest absolute value negative dividend
-
-                     if Lhi < 0 then
-                        S := Lhi;
-                     else
-                        S := -Uint_1;
-                     end if;
-
-                     --  Small negative value divided by large negative value
-                     --  generates a candidate for highest possible result.
-
-                     if Rlo < 0 then
-                        Max (Hi, Rlo / S);
-                     end if;
-
-                     --  Small negative value divided by large positive value
-                     --  generates a candidate for lowest possible result.
-
-                     if Rhi > 0 then
-                        Min (Lo, Rhi / S);
-                     end if;
-                  end if;
-
-                  --  Finally, if neither Lo or Hi set (happens if the right
-                  --  operand is always zero for example), then set 0 .. 0.
-
-                  if Lo = No_Uint and then Hi = No_Uint then
-                     Lo := Uint_0;
-                     Hi := Uint_0;
-
-                  --  If one bound set and not the other copy
-
-                  elsif Lo = No_Uint then
-                     Lo := Hi;
-
-                  elsif Hi = No_Uint then
-                     Hi := Lo;
-                  end if;
-               end;
-
             --  Exponentiation
 
             when N_Op_Expon =>
@@ -7264,14 +7197,15 @@
 
                else
                   --  High bound comes either from exponentiation of largest
-                  --  positive value to largest exponent value, or from the
-                  --  exponentiation of most negative value to an odd exponent.
+                  --  positive value to largest exponent value, or from
+                  --  the exponentiation of most negative value to an
+                  --  even exponent.
 
                   declare
                      Hi1, Hi2 : Uint;
 
                   begin
-                     if Lhi >= 0 then
+                     if Lhi > 0 then
                         Hi1 := Lhi ** Rhi;
                      else
                         Hi1 := Uint_0;
@@ -7279,9 +7213,9 @@
 
                      if Llo < 0 then
                         if Rhi mod 2 = 0 then
-                           Hi2 := Llo ** (Rhi - 1);
-                        else
                            Hi2 := Llo ** Rhi;
+                        else
+                           Hi2 := Llo ** (Rhi - 1);
                         end if;
                      else
                         Hi2 := Uint_0;
@@ -7316,7 +7250,7 @@
 
             when N_Op_Mod =>
                declare
-                  Maxabs : constant Uint := UI_Max (abs Rlo, abs Rhi);
+                  Maxabs : constant Uint := UI_Max (abs Rlo, abs Rhi) - 1;
                   --  This is the maximum absolute value of the result
 
                begin
@@ -7371,9 +7305,10 @@
 
             when N_Op_Rem =>
                declare
-                  Maxabs : constant Uint := UI_Max (abs Rlo, abs Rhi);
+                  Maxabs : constant Uint := UI_Max (abs Rlo, abs Rhi) - 1;
                   --  This is the maximum absolute value of the result. Note
-                  --  that the result range does not depend on the sign of B.
+                  --  that the result range does not depend on the sign of the
+                  --  right operand.
 
                begin
                   Lo := Uint_0;

Reply via email to