Patchwork [Ada] Avoid unnecessary use of Bignums for ELIMINATED mode

login
register
mail settings
Submitter Arnaud Charlet
Date Oct. 2, 2012, 8:05 a.m.
Message ID <20121002080519.GA27067@adacore.com>
Download mbox | patch
Permalink /patch/188419/
State New
Headers show

Comments

Arnaud Charlet - Oct. 2, 2012, 8:05 a.m.
Previously there were cases where the result of an operator was
converted to Bignum, only to be immediately converted back to
Long_Long_Integer with an overflow check. This patch removes
this unnecessary inefficiency.

The following program:

     1. procedure toplevov
     2.    (a : in out long_long_integer;
     3.     b : long_long_integer)
     4. is
     5. begin
     6.     a := b * b;
     7. end;

Now generates the following output when compiled
with -gnatG -gnato3.

procedure toplevov
 (a : in out long_long_integer;
  b : long_long_integer) is
begin
   a := long_long_integer(b) {*} long_long_integer(b);
   return;
end toplevov;

Previously it generated Bignum operations

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

2012-10-02  Robert Dewar  <dewar@adacore.com>

	* checks.ads, exp_ch4.adb, checks.adb
	(Minimize_Eliminate_Overflow_Checks): Add Top_Level parameter to avoid
	unnecessary conversions to Bignum.
	Minor reformatting.

Patch

Index: checks.adb
===================================================================
--- checks.adb	(revision 191921)
+++ checks.adb	(working copy)
@@ -1113,8 +1113,11 @@ 
 
       --  Otherwise, we have a top level arithmetic operator node, and this
       --  is where we commence the special processing for minimize/eliminate.
+      --  This is the case where we tell the machinery not to move into Bignum
+      --  mode at this top level (of course the top level operation will still
+      --  be in Bignum mode if either of its operands are of type Bignum).
 
-      Minimize_Eliminate_Overflow_Checks (Op, Lo, Hi);
+      Minimize_Eliminate_Overflow_Checks (Op, Lo, Hi, Top_Level => True);
 
       --  That call may but does not necessarily change the result type of Op.
       --  It is the job of this routine to undo such changes, so that at the
@@ -2333,23 +2336,24 @@ 
             Error_Msg_N
               ("\this will result in infinite recursion?", Parent (N));
             Insert_Action (N,
-               Make_Raise_Storage_Error
-                 (Sloc (N), Reason => SE_Infinite_Recursion));
+              Make_Raise_Storage_Error (Sloc (N),
+                Reason => SE_Infinite_Recursion));
 
+         --  Here for normal case of predicate active.
+
          else
-
             --  If the predicate is a static predicate and the operand is
             --  static, the predicate must be evaluated statically. If the
             --  evaluation fails this is a static constraint error.
 
             if Is_OK_Static_Expression (N) then
-               if  Present (Static_Predicate (Typ)) then
+               if Present (Static_Predicate (Typ)) then
                   if Eval_Static_Predicate_Check (N, Typ) then
                      return;
                   else
                      Error_Msg_NE
                        ("static expression fails static predicate check on&",
-                          N, Typ);
+                        N, Typ);
                   end if;
                end if;
             end if;
@@ -6549,9 +6553,10 @@ 
    ----------------------------------------
 
    procedure Minimize_Eliminate_Overflow_Checks
-     (N  : Node_Id;
-      Lo : out Uint;
-      Hi : out Uint)
+     (N         : Node_Id;
+      Lo        : out Uint;
+      Hi        : out Uint;
+      Top_Level : Boolean)
    is
       pragma Assert (Is_Signed_Integer_Type (Etype (N)));
 
@@ -6578,6 +6583,11 @@ 
       OK : Boolean;
       --  Used in call to Determine_Range
 
+      Bignum_Operands : Boolean;
+      --  Set True if one or more operands is already of type Bignum, meaning
+      --  that for sure (regardless of Top_Level setting) we are committed to
+      --  doing the operation in Bignum mode.
+
       procedure Max (A : in out Uint; B : Uint);
       --  If A is No_Uint, sets A to B, else to UI_Max (A, B);
 
@@ -6609,7 +6619,7 @@ 
    --  Start of processing for Minimize_Eliminate_Overflow_Checks
 
    begin
-      --  Case where we do not have an arithmetic operator.
+      --  Case where we do not have an arithmetic operator
 
       if not Is_Signed_Integer_Arithmetic_Op (N) then
 
@@ -6638,10 +6648,12 @@ 
       --  that lies below us!)
 
       else
-         Minimize_Eliminate_Overflow_Checks (Right_Opnd (N), Rlo, Rhi);
+         Minimize_Eliminate_Overflow_Checks
+           (Right_Opnd (N), Rlo, Rhi, Top_Level => False);
 
          if Binary then
-            Minimize_Eliminate_Overflow_Checks (Left_Opnd (N), Llo, Lhi);
+            Minimize_Eliminate_Overflow_Checks
+              (Left_Opnd (N), Llo, Lhi, Top_Level => False);
          end if;
       end if;
 
@@ -6650,10 +6662,13 @@ 
       if Rlo = No_Uint or else (Binary and then Llo = No_Uint) then
          Lo := No_Uint;
          Hi := No_Uint;
+         Bignum_Operands := True;
 
       --  Otherwise compute result range
 
       else
+         Bignum_Operands := False;
+
          case Nkind (N) is
 
             --  Absolute value
@@ -7007,15 +7022,34 @@ 
 
       if Lo = No_Uint or else Lo < LLLo or else Hi > LLHi then
 
-         --  In MINIMIZED mode, note that an overflow check is required
-         --  Note that we know we don't have a Bignum, since Bignums only
-         --  appear in Eliminated mode.
+         --  OK, we are definitely outside the range of Long_Long_Integer. The
+         --  question is whether to move into Bignum mode, or remain the domain
+         --  of Long_Long_Integer, signalling that an overflow check is needed.
 
-         if Check_Mode = Minimized then
+         --  Obviously in MINIMIZED mode we stay with LLI, since we are not in
+         --  the Bignum business. In ELIMINATED mode, we will normally move
+         --  into Bignum mode, but there is an exception if neither of our
+         --  operands is Bignum now, and we are at the top level (Top_Level
+         --  set True). In this case, there is no point in moving into Bignum
+         --  mode to prevent overflow if the caller will immediately convert
+         --  the Bignum value back to LLI with an overflow check. It's more
+         --  efficient to stay in LLI mode with an overflow check.
+
+         if Check_Mode = Minimized
+           or else (Top_Level and not Bignum_Operands)
+         then
             Enable_Overflow_Check (N);
 
-         --  Otherwise we are in ELIMINATED mode, switch to bignum
+            --  Since we are doing an overflow check, the result has to be in
+            --  Long_Long_Integer mode, so adjust the possible range to reflect
+            --  this. Note these calls also change No_Uint values from the top
+            --  level case to LLI bounds.
 
+            Max (Lo, LLLo);
+            Min (Hi, LLHi);
+
+         --  Otherwise we are in ELIMINATED mode and we switch to Bignum mode
+
          else
             pragma Assert (Check_Mode = Eliminated);
 
@@ -7079,6 +7113,11 @@ 
                    Name                   => New_Occurrence_Of (Fent, Loc),
                    Parameter_Associations => Args));
                Analyze_And_Resolve (N, RTE (RE_Bignum));
+
+               --  Indicate result is Bignum mode
+
+               Lo := No_Uint;
+               Hi := No_Uint;
                return;
             end;
          end if;
Index: checks.ads
===================================================================
--- checks.ads	(revision 191920)
+++ checks.ads	(working copy)
@@ -260,9 +260,10 @@ 
    --  parameter is used to supply Sloc values for the constructed tree.
 
    procedure Minimize_Eliminate_Overflow_Checks
-     (N  : Node_Id;
-      Lo : out Uint;
-      Hi : out Uint);
+     (N         : Node_Id;
+      Lo        : out Uint;
+      Hi        : out Uint;
+      Top_Level : Boolean);
    --  This is the main routine for handling MINIMIZED and ELIMINATED overflow
    --  checks. On entry N is a node whose result is a signed integer subtype.
    --  If the node is an artihmetic operation, then a range analysis is carried
@@ -321,6 +322,16 @@ 
    --
    --  Note that if Bignum values appear, the caller must take care of doing
    --  the appropriate mark/release operation on the secondary stack.
+   --
+   --  Top_Level is used to avoid inefficient unnecessary transitions into the
+   --  Bignum domain. If Top_Level is True, it means that the caller will have
+   --  to convert any Bignum value back to Long_Long_Integer, checking that the
+   --  value is in range. This is the normal case for a top level operator in
+   --  a subexpression. There is no point in going into Bignum mode to avoid an
+   --  overflow just so we can check for overflow the next moment. For calls
+   --  from comparisons and membership tests, and for all recursive calls, we
+   --  do want to transition into the Bignum domain if necessary. Note that
+   --  this setting is only relevant in ELIMINATED mode.
 
    -------------------------------------------------------
    -- Control and Optimization of Range/Overflow Checks --
Index: exp_ch4.adb
===================================================================
--- exp_ch4.adb	(revision 191920)
+++ exp_ch4.adb	(working copy)
@@ -2345,8 +2345,10 @@ 
       --  our operands using the Minimize_Eliminate circuitry which applies
       --  this processing to the two operand subtrees.
 
-      Minimize_Eliminate_Overflow_Checks (Left_Opnd (N),  Llo, Lhi);
-      Minimize_Eliminate_Overflow_Checks (Right_Opnd (N), Rlo, Rhi);
+      Minimize_Eliminate_Overflow_Checks
+        (Left_Opnd (N),  Llo, Lhi, Top_Level => False);
+      Minimize_Eliminate_Overflow_Checks
+        (Right_Opnd (N), Rlo, Rhi, Top_Level => False);
 
       --  See if the range information decides the result of the comparison
 
@@ -3735,7 +3737,7 @@ 
       --  Entity for Long_Long_Integer'Base (Standard should export this???)
 
    begin
-      Minimize_Eliminate_Overflow_Checks (Lop, Lo, Hi);
+      Minimize_Eliminate_Overflow_Checks (Lop, Lo, Hi, Top_Level => False);
 
       --  If right operand is a subtype name, and the subtype name has no
       --  predicate, then we can just replace the right operand with an
@@ -3760,8 +3762,10 @@ 
       --  have not been processed for minimized or eliminated checks.
 
       if Nkind (Rop) = N_Range then
-         Minimize_Eliminate_Overflow_Checks (Low_Bound (Rop),  Lo, Hi);
-         Minimize_Eliminate_Overflow_Checks (High_Bound (Rop), Lo, Hi);
+         Minimize_Eliminate_Overflow_Checks
+           (Low_Bound (Rop),  Lo, Hi, Top_Level => False);
+         Minimize_Eliminate_Overflow_Checks
+           (High_Bound (Rop), Lo, Hi, Top_Level => False);
 
          --  We have A in B .. C, treated as  A >= B and then A <= C