[Ada] Use static stack allocation for small string if-expressions
This changes the expanded code generated for if-expressions of 1-dimensional
arrays to create a static temporary on the stack if a small upper bound can
be computed for the length of a subtype covering the result. Static stack
allocation is preferred over dynamic allocation for code generation purpose.
This also contains a couple of enhancements to the support code for checks,
so as to avoid generating useless checks during the modified expansion.
gcc/ada/
* checks.adb (Apply_Length_Check_On_Assignment): Return early if
the Suppress_Assignment_Checks flag is set.
(Selected_Range_Checks): Deal with conditional expressions.
* exp_ch4.adb (Too_Large_Length_For_Array): New constant.
(Expand_Concatenate): Use it in lieu of Too_Large_Max_Length.
(Expand_N_If_Expression): If the result has a unidimensional array
type but the dependent expressions have constrained subtypes with
known bounds, create a static temporary on the stack with a subtype
covering the result.
(Get_First_Index_Bounds): Deal with string literals.
* uintp.ads (Uint_256): New deferred constant.
* sinfo.ads (Suppress_Assignment_Checks): Document new usage.
diff --git a/gcc/ada/checks.adb b/gcc/ada/checks.adb
index 204d13e..22577c8 100644
--- a/gcc/ada/checks.adb
+++ b/gcc/ada/checks.adb
@@ -2297,6 +2297,15 @@
Assign : constant Node_Id := Parent (Target);
begin
+ -- Do not apply length checks if parent is still an assignment statement
+ -- with Suppress_Assignment_Checks flag set.
+
+ if Nkind (Assign) = N_Assignment_Statement
+ and then Suppress_Assignment_Checks (Assign)
+ then
+ return;
+ end if;
+
-- No check is needed for the initialization of an object whose
-- nominal subtype is unconstrained.
@@ -6462,7 +6471,7 @@
end if;
-- Do not set range check flag if parent is assignment statement or
- -- object declaration with Suppress_Assignment_Checks flag set
+ -- object declaration with Suppress_Assignment_Checks flag set.
if Nkind (Parent (N)) in N_Assignment_Statement | N_Object_Declaration
and then Suppress_Assignment_Checks (Parent (N))
@@ -10500,6 +10509,11 @@
-- Returns expression to compute:
-- N'First or N'Last using Duplicate_Subexpr_No_Checks
+ function Is_Cond_Expr_Ge (N : Node_Id; V : Node_Id) return Boolean;
+ function Is_Cond_Expr_Le (N : Node_Id; V : Node_Id) return Boolean;
+ -- Return True if N is a conditional expression whose dependent
+ -- expressions are all known and greater/lower than or equal to V.
+
function Range_E_Cond
(Exptyp : Entity_Id;
Typ : Entity_Id;
@@ -10522,6 +10536,16 @@
-- Return expression to compute:
-- Exp'First < Typ'First or else Exp'Last > Typ'Last
+ function "<" (Left, Right : Node_Id) return Boolean
+ is (if Is_Floating_Point_Type (S_Typ)
+ then Expr_Value_R (Left) < Expr_Value_R (Right)
+ else Expr_Value (Left) < Expr_Value (Right));
+ function "<=" (Left, Right : Node_Id) return Boolean
+ is (if Is_Floating_Point_Type (S_Typ)
+ then Expr_Value_R (Left) <= Expr_Value_R (Right)
+ else Expr_Value (Left) <= Expr_Value (Right));
+ -- Convenience comparison functions of integer or floating point values
+
---------------
-- Add_Check --
---------------
@@ -10702,6 +10726,60 @@
Make_Integer_Literal (Loc, Indx)));
end Get_N_Last;
+ ---------------------
+ -- Is_Cond_Expr_Ge --
+ ---------------------
+
+ function Is_Cond_Expr_Ge (N : Node_Id; V : Node_Id) return Boolean is
+ begin
+ -- Only if expressions are relevant for the time being
+
+ if Nkind (N) = N_If_Expression then
+ declare
+ Cond : constant Node_Id := First (Expressions (N));
+ Thenx : constant Node_Id := Next (Cond);
+ Elsex : constant Node_Id := Next (Thenx);
+
+ begin
+ return Compile_Time_Known_Value (Thenx)
+ and then V <= Thenx
+ and then
+ ((Compile_Time_Known_Value (Elsex) and then V <= Elsex)
+ or else Is_Cond_Expr_Ge (Elsex, V));
+ end;
+
+ else
+ return False;
+ end if;
+ end Is_Cond_Expr_Ge;
+
+ ---------------------
+ -- Is_Cond_Expr_Le --
+ ---------------------
+
+ function Is_Cond_Expr_Le (N : Node_Id; V : Node_Id) return Boolean is
+ begin
+ -- Only if expressions are relevant for the time being
+
+ if Nkind (N) = N_If_Expression then
+ declare
+ Cond : constant Node_Id := First (Expressions (N));
+ Thenx : constant Node_Id := Next (Cond);
+ Elsex : constant Node_Id := Next (Thenx);
+
+ begin
+ return Compile_Time_Known_Value (Thenx)
+ and then Thenx <= V
+ and then
+ ((Compile_Time_Known_Value (Elsex) and then Elsex <= V)
+ or else Is_Cond_Expr_Le (Elsex, V));
+ end;
+
+ else
+ return False;
+ end if;
+ end Is_Cond_Expr_Le;
+
------------------
-- Range_E_Cond --
------------------
@@ -10783,13 +10861,6 @@
Get_E_First_Or_Last (Loc, Typ, Indx, Name_Last)));
end Range_N_Cond;
- function "<" (Left, Right : Node_Id) return Boolean
- is (if Is_Floating_Point_Type (S_Typ)
- then Expr_Value_R (Left) < Expr_Value_R (Right)
- else Expr_Value (Left) < Expr_Value (Right));
- -- Convenience comparison function of integer or floating point
- -- values.
-
-- Start of processing for Selected_Range_Checks
begin
@@ -10885,6 +10956,14 @@
then
LB := T_LB;
Known_LB := True;
+
+ -- Similarly; deal with the case where the low bound is a
+ -- conditional expression whose result is greater than or
+ -- equal to the target low bound.
+
+ elsif Is_Cond_Expr_Ge (LB, T_LB) then
+ LB := T_LB;
+ Known_LB := True;
end if;
-- Likewise for the high bound
@@ -10897,6 +10976,10 @@
then
HB := T_HB;
Known_HB := True;
+
+ elsif Is_Cond_Expr_Le (HB, T_HB) then
+ HB := T_HB;
+ Known_HB := True;
end if;
end if;
diff --git a/gcc/ada/exp_ch4.adb b/gcc/ada/exp_ch4.adb
index cf29fb7..288ce9a9 100644
--- a/gcc/ada/exp_ch4.adb
+++ b/gcc/ada/exp_ch4.adb
@@ -81,6 +81,10 @@
package body Exp_Ch4 is
+ Too_Large_Length_For_Array : constant Unat := Uint_256;
+ -- Threshold from which we do not try to create static array temporaries in
+ -- order to eliminate dynamic stack allocations.
+
-----------------------
-- Local Subprograms --
-----------------------
@@ -2693,9 +2697,6 @@
-- this loop is complete, always contains the last operand (which is not
-- the same as Operands (NN), since null operands are skipped).
- Too_Large_Max_Length : constant Unat := UI_From_Int (256);
- -- Threshold from which the computation of maximum lengths is useless
-
-- Arrays describing the operands, only the first NN entries of each
-- array are set (NN < N when we exclude known null operands).
@@ -2711,9 +2712,9 @@
-- corresponding entry in Is_Fixed_Length is True.
Max_Length : array (1 .. N) of Unat;
- -- Set to the maximum length of operand, or Too_Large_Max_Length if it
- -- is not known. Entries in this array are set only if the corresponding
- -- entry in Is_Fixed_Length is False;
+ -- Set to the maximum length of operand, or Too_Large_Length_For_Array
+ -- if it is not known. Entries in this array are set only if the
+ -- corresponding entry in Is_Fixed_Length is False;
Opnd_Low_Bound : array (1 .. N) of Node_Id;
-- Set to lower bound of operand. Either an integer literal in the case
@@ -2733,9 +2734,9 @@
-- to just do a Copy_Node to get an appropriate copy. The extra zeroth
-- entry always is set to zero. The length is of type Artyp.
- Max_Aggr_Length : Unat := Too_Large_Max_Length;
- -- Set to the maximum total length, or at least Too_Large_Max_Length if
- -- it is not known.
+ Max_Aggr_Length : Unat := Too_Large_Length_For_Array;
+ -- Set to the maximum total length, or Too_Large_Length_For_Array at
+ -- least if it is not known.
Low_Bound : Node_Id := Empty;
-- A tree node representing the low bound of the result (of type Ityp).
@@ -3115,7 +3116,7 @@
end;
else
- Max_Length (NN) := Too_Large_Max_Length;
+ Max_Length (NN) := Too_Large_Length_For_Array;
end if;
Append_To (Actions,
@@ -3362,7 +3363,7 @@
if Compile_Time_Known_Value (Low_Bound)
and then not Compile_Time_Known_Value (High_Bound)
- and then Max_Aggr_Length < Too_Large_Max_Length
+ and then Max_Aggr_Length < Too_Large_Length_For_Array
then
declare
Known_High_Bound : constant Node_Id :=
@@ -5860,19 +5861,43 @@
Elsex : constant Node_Id := Next (Thenx);
Typ : constant Entity_Id := Etype (N);
- Actions : List_Id;
- Decl : Node_Id;
- Expr : Node_Id;
- New_If : Node_Id;
- New_N : Node_Id;
-
+ Force_Expand : constant Boolean := Is_Anonymous_Access_Actual (N);
-- Determine if we are dealing with a special case of a conditional
-- expression used as an actual for an anonymous access type which
-- forces us to transform the if expression into an expression with
-- actions in order to create a temporary to capture the level of the
-- expression in each branch.
- Force_Expand : constant Boolean := Is_Anonymous_Access_Actual (N);
+ function OK_For_Single_Subtype (T1, T2 : Entity_Id) return Boolean;
+ -- Return true if it is acceptable to use a single subtype for two
+ -- dependent expressions of subtype T1 and T2 respectively, which are
+ -- unidimensional arrays whose index bounds are known at compile time.
+
+ ---------------------------
+ -- OK_For_Single_Subtype --
+ ---------------------------
+
+ function OK_For_Single_Subtype (T1, T2 : Entity_Id) return Boolean is
+ Lo1, Hi1 : Uint;
+ Lo2, Hi2 : Uint;
+
+ begin
+ Get_First_Index_Bounds (T1, Lo1, Hi1);
+ Get_First_Index_Bounds (T2, Lo2, Hi2);
+
+ -- Return true if the length of the covering subtype is not too large
+
+ return
+ UI_Max (Hi1, Hi2) - UI_Min (Lo1, Lo2) < Too_Large_Length_For_Array;
+ end OK_For_Single_Subtype;
+
+ -- Local variables
+
+ Actions : List_Id;
+ Decl : Node_Id;
+ Expr : Node_Id;
+ New_If : Node_Id;
+ New_N : Node_Id;
-- Start of processing for Expand_N_If_Expression
@@ -6049,6 +6074,223 @@
Prefix => New_Occurrence_Of (Cnn, Loc));
end;
+ -- If the result is a unidimensional unconstrained array but the two
+ -- dependent expressions have constrained subtypes with known bounds,
+ -- then we expand as follows:
+
+ -- subtype Txx is Typ (<static low-bound> .. <static high-bound>);
+ -- Cnn : Txx;
+ -- if cond then
+ -- <<then actions>>
+ -- Cnn (<then low-bound .. then high-bound>) := then-expr;
+ -- else
+ -- <<else actions>>
+ -- Cnn (<else low bound .. else high-bound>) := else-expr;
+ -- end if;
+
+ -- and replace the if expression by a slice of Cnn, provided that Txx
+ -- is not too large. This will create a static temporary instead of the
+ -- dynamic one of the next case and thus help the code generator.
+
+ -- Note that we need to deal with the case where the else expression is
+ -- itself such a slice, in order to catch if expressions with more than
+ -- two dependent expressions in the source code.
+
+ elsif Is_Array_Type (Typ)
+ and then Number_Dimensions (Typ) = 1
+ and then not Is_Constrained (Typ)
+ and then Is_Constrained (Etype (Thenx))
+ and then Compile_Time_Known_Bounds (Etype (Thenx))
+ and then
+ ((Is_Constrained (Etype (Elsex))
+ and then Compile_Time_Known_Bounds (Etype (Elsex))
+ and then OK_For_Single_Subtype (Etype (Thenx), Etype (Elsex)))
+ or else
+ (Nkind (Elsex) = N_Slice
+ and then Is_Constrained (Etype (Prefix (Elsex)))
+ and then Compile_Time_Known_Bounds (Etype (Prefix (Elsex)))
+ and then
+ OK_For_Single_Subtype (Etype (Thenx), Etype (Prefix (Elsex)))))
+ and then not Generate_C_Code
+ then
+ declare
+ Ityp : constant Entity_Id := Base_Type (Etype (First_Index (Typ)));
+
+ function Build_New_Bound
+ (Then_Bnd : Uint;
+ Else_Bnd : Uint;
+ Slice_Bnd : Node_Id) return Node_Id;
+ -- Build a new bound from the bounds of the if expression
+
+ function To_Ityp (V : Uint) return Node_Id;
+ -- Convert V to an index value in Ityp
+
+ ---------------------
+ -- Build_New_Bound --
+ ---------------------
+
+ function Build_New_Bound
+ (Then_Bnd : Uint;
+ Else_Bnd : Uint;
+ Slice_Bnd : Node_Id) return Node_Id is
+
+ begin
+ if Nkind (Elsex) = N_Slice then
+ if Compile_Time_Known_Value (Slice_Bnd)
+ and then Expr_Value (Slice_Bnd) = Then_Bnd
+ then
+ return To_Ityp (Then_Bnd);
+
+ else
+ return Make_If_Expression (Loc,
+ Expressions => New_List (
+ Duplicate_Subexpr (Cond),
+ To_Ityp (Then_Bnd),
+ New_Copy_Tree (Slice_Bnd)));
+ end if;
+
+ elsif Then_Bnd = Else_Bnd then
+ return To_Ityp (Then_Bnd);
+
+ else
+ return Make_If_Expression (Loc,
+ Expressions => New_List (
+ Duplicate_Subexpr (Cond),
+ To_Ityp (Then_Bnd),
+ To_Ityp (Else_Bnd)));
+ end if;
+ end Build_New_Bound;
+
+ -------------
+ -- To_Ityp --
+ -------------
+
+ function To_Ityp (V : Uint) return Node_Id is
+ Result : constant Node_Id := Make_Integer_Literal (Loc, V);
+
+ begin
+ if Is_Enumeration_Type (Ityp) then
+ return
+ Make_Attribute_Reference (Loc,
+ Prefix => New_Occurrence_Of (Ityp, Loc),
+ Attribute_Name => Name_Val,
+ Expressions => New_List (Result));
+ else
+ return Result;
+ end if;
+ end To_Ityp;
+
+ Ent : Node_Id;
+ Slice_Lo, Slice_Hi : Node_Id;
+ Subtyp_Ind : Node_Id;
+ Else_Lo, Else_Hi : Uint;
+ Min_Lo, Max_Hi : Uint;
+ Then_Lo, Then_Hi : Uint;
+ Then_List, Else_List : List_Id;
+
+ begin
+ Get_First_Index_Bounds (Etype (Thenx), Then_Lo, Then_Hi);
+
+ if Nkind (Elsex) = N_Slice then
+ Slice_Lo := Low_Bound (Discrete_Range (Elsex));
+ Slice_Hi := High_Bound (Discrete_Range (Elsex));
+ Get_First_Index_Bounds
+ (Etype (Prefix (Elsex)), Else_Lo, Else_Hi);
+
+ else
+ Slice_Lo := Empty;
+ Slice_Hi := Empty;
+ Get_First_Index_Bounds (Etype (Elsex), Else_Lo, Else_Hi);
+ end if;
+
+ Min_Lo := UI_Min (Then_Lo, Else_Lo);
+ Max_Hi := UI_Max (Then_Hi, Else_Hi);
+
+ -- Now we construct an array object with appropriate bounds and
+ -- mark it as internal to prevent useless initialization when
+ -- Initialize_Scalars is enabled. Also since this is the actual
+ -- result entity, we make sure we have debug information for it.
+
+ Subtyp_Ind :=
+ Make_Subtype_Indication (Loc,
+ Subtype_Mark => New_Occurrence_Of (Typ, Loc),
+ Constraint =>
+ Make_Index_Or_Discriminant_Constraint (Loc,
+ Constraints => New_List (
+ Make_Range (Loc,
+ Low_Bound => To_Ityp (Min_Lo),
+ High_Bound => To_Ityp (Max_Hi)))));
+
+ Ent := Make_Temporary (Loc, 'C');
+ Set_Is_Internal (Ent);
+ Set_Debug_Info_Needed (Ent);
+
+ Decl :=
+ Make_Object_Declaration (Loc,
+ Defining_Identifier => Ent,
+ Object_Definition => Subtyp_Ind);
+
+ -- If the result of the expression appears as the initializing
+ -- expression of an object declaration, we can just rename the
+ -- result, rather than copying it.
+
+ Mutate_Ekind (Ent, E_Variable);
+ Set_OK_To_Rename (Ent);
+
+ Then_List := New_List (
+ Make_Assignment_Statement (Loc,
+ Name =>
+ Make_Slice (Loc,
+ Prefix => New_Occurrence_Of (Ent, Loc),
+ Discrete_Range =>
+ Make_Range (Loc,
+ Low_Bound => To_Ityp (Then_Lo),
+ High_Bound => To_Ityp (Then_Hi))),
+ Expression => Relocate_Node (Thenx)));
+
+ Set_Suppress_Assignment_Checks (Last (Then_List));
+
+ if Nkind (Elsex) = N_Slice then
+ Else_List := New_List (
+ Make_Assignment_Statement (Loc,
+ Name =>
+ Make_Slice (Loc,
+ Prefix => New_Occurrence_Of (Ent, Loc),
+ Discrete_Range =>
+ Make_Range (Loc,
+ Low_Bound => New_Copy_Tree (Slice_Lo),
+ High_Bound => New_Copy_Tree (Slice_Hi))),
+ Expression => Relocate_Node (Elsex)));
+
+ else
+ Else_List := New_List (
+ Make_Assignment_Statement (Loc,
+ Name =>
+ Make_Slice (Loc,
+ Prefix => New_Occurrence_Of (Ent, Loc),
+ Discrete_Range =>
+ Make_Range (Loc,
+ Low_Bound => To_Ityp (Else_Lo),
+ High_Bound => To_Ityp (Else_Hi))),
+ Expression => Relocate_Node (Elsex)));
+ end if;
+
+ Set_Suppress_Assignment_Checks (Last (Else_List));
+
+ New_If :=
+ Make_Implicit_If_Statement (N,
+ Condition => Duplicate_Subexpr (Cond),
+ Then_Statements => Then_List,
+ Else_Statements => Else_List);
+
+ New_N :=
+ Make_Slice (Loc,
+ Prefix => New_Occurrence_Of (Ent, Loc),
+ Discrete_Range => Make_Range (Loc,
+ Low_Bound => Build_New_Bound (Then_Lo, Else_Lo, Slice_Lo),
+ High_Bound => Build_New_Bound (Then_Hi, Else_Hi, Slice_Hi)));
+ end;
+
-- If the result is an unconstrained array and the if expression is in a
-- context other than the initializing expression of the declaration of
-- an object, then we pull out the if expression as follows:
@@ -6223,7 +6465,7 @@
end if;
-- For the sake of GNATcoverage, generate an intermediate temporary in
- -- the case where the if-expression is a condition in an outer decision,
+ -- the case where the if expression is a condition in an outer decision,
-- in order to make sure that no branch is shared between the decisions.
elsif Opt.Suppress_Control_Flow_Optimizations
@@ -13400,10 +13642,16 @@
-- This follows Sem_Eval.Compile_Time_Known_Bounds
- Typ := Underlying_Type (Etype (First_Index (T)));
+ if Ekind (T) = E_String_Literal_Subtype then
+ Lo := Expr_Value (String_Literal_Low_Bound (T));
+ Hi := Lo + String_Literal_Length (T) - 1;
- Lo := Expr_Value (Type_Low_Bound (Typ));
- Hi := Expr_Value (Type_High_Bound (Typ));
+ else
+ Typ := Underlying_Type (Etype (First_Index (T)));
+
+ Lo := Expr_Value (Type_Low_Bound (Typ));
+ Hi := Expr_Value (Type_High_Bound (Typ));
+ end if;
end Get_First_Index_Bounds;
------------------------
diff --git a/gcc/ada/sinfo.ads b/gcc/ada/sinfo.ads
index 57c6438..a9099e3 100644
--- a/gcc/ada/sinfo.ads
+++ b/gcc/ada/sinfo.ads
@@ -2299,7 +2299,7 @@
-- can be set in N_Object_Declaration nodes, to similarly suppress any
-- checks on the initializing value. In assignment statements it also
-- suppresses access checks in the generated code for out- and in-out
- -- parameters in entry calls.
+ -- parameters in entry calls, as well as length checks.
-- Suppress_Loop_Warnings
-- Used in N_Loop_Statement node to indicate that warnings within the
diff --git a/gcc/ada/uintp.ads b/gcc/ada/uintp.ads
index 55f5b97..1b408fc 100644
--- a/gcc/ada/uintp.ads
+++ b/gcc/ada/uintp.ads
@@ -70,6 +70,7 @@
Uint_80 : constant Uint;
Uint_127 : constant Uint;
Uint_128 : constant Uint;
+ Uint_256 : constant Uint;
Uint_Minus_1 : constant Uint;
Uint_Minus_2 : constant Uint;
@@ -507,6 +508,7 @@
Uint_80 : constant Uint := Uint (Uint_Direct_Bias + 80);
Uint_127 : constant Uint := Uint (Uint_Direct_Bias + 127);
Uint_128 : constant Uint := Uint (Uint_Direct_Bias + 128);
+ Uint_256 : constant Uint := Uint (Uint_Direct_Bias + 256);
Uint_Minus_1 : constant Uint := Uint (Uint_Direct_Bias - 1);
Uint_Minus_2 : constant Uint := Uint (Uint_Direct_Bias - 2);