https://gcc.gnu.org/g:a7c5e316e28c993952337ea7a5570cb5d1df1daa

commit r16-1156-ga7c5e316e28c993952337ea7a5570cb5d1df1daa
Author: Bob Duff <d...@adacore.com>
Date:   Tue Feb 4 14:36:03 2025 -0500

    ada: Improve efficiency of very large shift counts
    
    For a call to an intrinsic shift function with a large Amount, for
    example Shift_Right(..., Amount => Natural'Last), and a
    compile-time-known value, the compiler would take an absurdly long time
    to compute the value. This patch fixes that by special-casing shift
    counts that are larger than the size of the thing being shifted.
    
    gcc/ada/ChangeLog:
    
            * sem_eval.adb (Fold_Shift): If the Amount parameter is greater
            than the size in bits, use the size. For example, if we are
            shifting an Unsigned_8 value, then Amount => 1_000_001 gives the
            same result as Amount => 8. This change avoids computing the value
            of 2**1_000_000, which takes too long and uses too much memory.
            Note that the computation we're talking about is a compile-time
            computation. Minor cleanup. DRY.
            * sem_eval.ads (Fold_Str, Fold_Uint, Fold_Ureal): Fold the
            comments into one comment, because DRY. Remove useless
            verbiage.

Diff:
---
 gcc/ada/sem_eval.adb | 95 ++++++++++++++++++++++++++--------------------------
 gcc/ada/sem_eval.ads | 37 +++-----------------
 2 files changed, 51 insertions(+), 81 deletions(-)

diff --git a/gcc/ada/sem_eval.adb b/gcc/ada/sem_eval.adb
index b7dfe01f2973..5d1506364956 100644
--- a/gcc/ada/sem_eval.adb
+++ b/gcc/ada/sem_eval.adb
@@ -4989,27 +4989,41 @@ package body Sem_Eval is
          end if;
       end Check_Elab_Call;
 
-      Modulus, Val : Uint;
-
    begin
-      if Compile_Time_Known_Value (Left)
-        and then Compile_Time_Known_Value (Right)
+      if not (Compile_Time_Known_Value (Left)
+              and then Compile_Time_Known_Value (Right))
       then
-         pragma Assert (not Non_Binary_Modulus (Typ));
+         return;
+      end if;
+
+      pragma Assert (not Non_Binary_Modulus (Typ));
+      pragma Assert (Expr_Value (Right) >= Uint_0); -- Amount is always Natural
+
+      --  Shift by zero bits is a no-op
 
+      if Expr_Value (Right) = Uint_0 then
+         Fold_Uint (N, Expr_Value (Left), Static => Static);
+         return;
+      end if;
+
+      declare
+         Modulus : constant Uint :=
+           (if Is_Modular_Integer_Type (Typ) then Einfo.Entities.Modulus (Typ)
+            else Uint_2 ** RM_Size (Typ));
+         Amount : constant Uint := UI_Min (Expr_Value (Right), RM_Size (Typ));
+         --  Shift by an Amount greater than the size is all-zeros or all-ones.
+         --  Without this "min", we could use huge amounts of time and memory
+         --  below (e.g. 2**Amount, if Amount were a billion).
+
+         Val : Uint;
+      begin
          if Op = N_Op_Shift_Left then
             Check_Elab_Call;
 
-            if Is_Modular_Integer_Type (Typ) then
-               Modulus := Einfo.Entities.Modulus (Typ);
-            else
-               Modulus := Uint_2 ** RM_Size (Typ);
-            end if;
-
             --  Fold Shift_Left (X, Y) by computing
             --  (X * 2**Y) rem modulus [- Modulus]
 
-            Val := (Expr_Value (Left) * (Uint_2 ** Expr_Value (Right)))
+            Val := (Expr_Value (Left) * (Uint_2 ** Amount))
                      rem Modulus;
 
             if Is_Modular_Integer_Type (Typ)
@@ -5023,49 +5037,32 @@ package body Sem_Eval is
          elsif Op = N_Op_Shift_Right then
             Check_Elab_Call;
 
-            --  X >> 0 is a no-op
+            --  Fold X >> Y by computing (X [+ Modulus]) / 2**Y.
+            --  Note that after a Shift_Right operation (with Y > 0), the
+            --  result is always positive, even if the original operand was
+            --  negative.
 
-            if Expr_Value (Right) = Uint_0 then
-               Fold_Uint (N, Expr_Value (Left), Static => Static);
-            else
-               if Is_Modular_Integer_Type (Typ) then
-                  Modulus := Einfo.Entities.Modulus (Typ);
+            declare
+               M : Unat;
+            begin
+               if Expr_Value (Left) >= Uint_0 then
+                  M := Uint_0;
                else
-                  Modulus := Uint_2 ** RM_Size (Typ);
+                  M := Modulus;
                end if;
 
-               --  Fold X >> Y by computing (X [+ Modulus]) / 2**Y
-               --  Note that after a Shift_Right operation (with Y > 0), the
-               --  result is always positive, even if the original operand was
-               --  negative.
-
-               declare
-                  M : Unat;
-               begin
-                  if Expr_Value (Left) >= Uint_0 then
-                     M := Uint_0;
-                  else
-                     M := Modulus;
-                  end if;
+               Fold_Uint
+                 (N,
+                  (Expr_Value (Left) + M) / (Uint_2 ** Amount),
+                  Static => Static);
+            end;
 
-                  Fold_Uint
-                    (N,
-                     (Expr_Value (Left) + M) / (Uint_2 ** Expr_Value (Right)),
-                     Static => Static);
-               end;
-            end if;
          elsif Op = N_Op_Shift_Right_Arithmetic then
             Check_Elab_Call;
 
             declare
-               Two_Y : constant Uint := Uint_2 ** Expr_Value (Right);
+               Two_Y : constant Uint := Uint_2 ** Amount;
             begin
-               if Is_Modular_Integer_Type (Typ) then
-                  Modulus := Einfo.Entities.Modulus (Typ);
-               else
-                  Modulus := Uint_2 ** RM_Size (Typ);
-               end if;
-
                --  X / 2**Y if X if positive or a small enough modular integer
 
                if (Is_Modular_Integer_Type (Typ)
@@ -5096,7 +5093,7 @@ package body Sem_Eval is
                     (N,
                      (Expr_Value (Left)) / Two_Y
                         + (Two_Y - Uint_1)
-                          * Uint_2 ** (RM_Size (Typ) - Expr_Value (Right)),
+                          * Uint_2 ** (RM_Size (Typ) - Amount),
                      Static => Static);
 
                --  Negative signed integer, compute via multiple/divide the
@@ -5108,13 +5105,15 @@ package body Sem_Eval is
                     (N,
                      (Modulus + Expr_Value (Left)) / Two_Y
                         + (Two_Y - Uint_1)
-                          * Uint_2 ** (RM_Size (Typ) - Expr_Value (Right))
+                          * Uint_2 ** (RM_Size (Typ) - Amount)
                         - Modulus,
                      Static => Static);
                end if;
             end;
+         else
+            raise Program_Error;
          end if;
-      end if;
+      end;
    end Fold_Shift;
 
    --------------
diff --git a/gcc/ada/sem_eval.ads b/gcc/ada/sem_eval.ads
index 138278f7af3a..7df1c4c25e91 100644
--- a/gcc/ada/sem_eval.ads
+++ b/gcc/ada/sem_eval.ads
@@ -342,41 +342,12 @@ package Sem_Eval is
    --  set of messages is all posted.
 
    procedure Fold_Str (N : Node_Id; Val : String_Id; Static : Boolean);
-   --  Rewrite N with a new N_String_Literal node as the result of the compile
-   --  time evaluation of the node N. Val is the resulting string value from
-   --  the folding operation. The Is_Static_Expression flag is set in the
-   --  result node. The result is fully analyzed and resolved. Static indicates
-   --  whether the result should be considered static or not (True = consider
-   --  static). The point here is that normally all string literals are static,
-   --  but if this was the result of some sequence of evaluation where values
-   --  were known at compile time but not static, then the result is not
-   --  static. The call has no effect if Raises_Constraint_Error (N) is True,
-   --  since there is no point in folding if we have an error.
-
    procedure Fold_Uint (N : Node_Id; Val : Uint; Static : Boolean);
-   --  Rewrite N with a (N_Integer_Literal, N_Identifier, N_Character_Literal)
-   --  node as the result of the compile time evaluation of the node N. Val is
-   --  the result in the integer case and is the position of the literal in the
-   --  literals list for the enumeration case. Is_Static_Expression is set True
-   --  in the result node. The result is fully analyzed/resolved. Static
-   --  indicates whether the result should be considered static or not (True =
-   --  consider static). The point here is that normally all integer literals
-   --  are static, but if this was the result of some sequence of evaluation
-   --  where values were known at compile time but not static, then the result
-   --  is not static. The call has no effect if Raises_Constraint_Error (N) is
-   --  True, since there is no point in folding if we have an error.
-
    procedure Fold_Ureal (N : Node_Id; Val : Ureal; Static : Boolean);
-   --  Rewrite N with a new N_Real_Literal node as the result of the compile
-   --  time evaluation of the node N. Val is the resulting real value from the
-   --  folding operation. The Is_Static_Expression flag is set in the result
-   --  node. The result is fully analyzed and result. Static indicates whether
-   --  the result should be considered static or not (True = consider static).
-   --  The point here is that normally all string literals are static, but if
-   --  this was the result of some sequence of evaluation where values were
-   --  known at compile time but not static, then the result is not static.
-   --  The call has no effect if Raises_Constraint_Error (N) is True, since
-   --  there is no point in folding if we have an error.
+   --  Rewrite N with a new literal node with compile-time-known value Val.
+   --  Is_Static_Expression is set to Static. This has no effect if
+   --  Raises_Constraint_Error (N) is True, since there is no point in
+   --  folding if we have an error.
 
    procedure Fold (N : Node_Id);
    --  Rewrite N with the relevant value if Compile_Time_Known_Value (N) is

Reply via email to