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;