From patchwork Tue Oct 2 08:05:19 2012
ContentType: text/plain; charset="utf8"
MIMEVersion: 1.0
ContentTransferEncoding: 7bit
Subject: [Ada] Avoid unnecessary use of Bignums for ELIMINATED mode
From: Arnaud Charlet
XPatchworkId: 188419
MessageId: <20121002080519.GA27067@adacore.com>
To: gccpatches@gcc.gnu.org
Cc: Robert Dewar
Date: Tue, 2 Oct 2012 04:05:19 0400
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_64pclinuxgnu, committed on trunk
20121002 Robert Dewar
* checks.ads, exp_ch4.adb, checks.adb
(Minimize_Eliminate_Overflow_Checks): Add Top_Level parameter to avoid
unnecessary conversions to Bignum.
Minor reformatting.
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