[Ada] Performance degradation with references

This patch modifies the creation of markers for variable references in the
context of SPARK elaboration checks. Previously, prior to checking whether a
reference requires such a marker, the compiler performed a logarithmic look up
to determine whether the reference appears within a call. This action caused
the compiler to degrade when a source program contains multiple (100,000s)
references. Now, the compiler no longer performs the look up immediately.

2018-06-11  Hristian Kirtchev  <kirtchev@adacore.com>

gcc/ada/

	* sem_ch8.adb (Find_Direct_Name): Mode the declaration of
	Is_Assignment_LHS further in. Use predicate
	Needs_Variable_Reference_Marker to determine whether to create a
	variable marker.
	(Find_Expanded_Name): Mode the declaration of Is_Assignment_LHS further
	in.  Use predicate Needs_Variable_Reference_Marker to determine whether
	to create a variable marker.
	* sem_elab.adb (Build_Variable_Reference_Marker): Remove the various
	checks that determine whether the identifier or expanded name is a
	suitable variable reference.  The checks are now performed by
	Needs_Variable_Reference_Marker.
	* sem_res.adb (Resolve_Actuals): Use predicate
	Needs_Variable_Reference_Marker to determine whether to create a
	variable marker.
	* sem_util.adb (Needs_Variable_Reference_Marker): New routine.
	* sem_util.ads (Needs_Variable_Reference_Marker): New routine.

From-SVN: r261400
This commit is contained in:
Hristian Kirtchev 2018-06-11 09:16:54 +00:00 committed by Pierre-Marie de Rodat
parent 51b84f99dc
commit 692918a9c1
6 changed files with 206 additions and 175 deletions

View File

@ -1,3 +1,22 @@
2018-06-11 Hristian Kirtchev <kirtchev@adacore.com>
* sem_ch8.adb (Find_Direct_Name): Mode the declaration of
Is_Assignment_LHS further in. Use predicate
Needs_Variable_Reference_Marker to determine whether to create a
variable marker.
(Find_Expanded_Name): Mode the declaration of Is_Assignment_LHS further
in. Use predicate Needs_Variable_Reference_Marker to determine whether
to create a variable marker.
* sem_elab.adb (Build_Variable_Reference_Marker): Remove the various
checks that determine whether the identifier or expanded name is a
suitable variable reference. The checks are now performed by
Needs_Variable_Reference_Marker.
* sem_res.adb (Resolve_Actuals): Use predicate
Needs_Variable_Reference_Marker to determine whether to create a
variable marker.
* sem_util.adb (Needs_Variable_Reference_Marker): New routine.
* sem_util.ads (Needs_Variable_Reference_Marker): New routine.
2018-06-11 Valentine Reboul <reboul@adacore.com>
* doc/gnat_rm.rst, doc/gnat_ugn.rst: Rename "GPL Edition" into

View File

@ -5423,8 +5423,6 @@ package body Sem_Ch8 is
-- Local variables
Is_Assignment_LHS : constant Boolean := Is_LHS (N) = Yes;
Nested_Inst : Entity_Id := Empty;
-- The entity of a nested instance which appears within Inst (if any)
@ -5970,11 +5968,19 @@ package body Sem_Ch8 is
-- reference is a write when it appears on the left hand side of an
-- assignment.
if not Within_Subprogram_Call (N) then
Build_Variable_Reference_Marker
(N => N,
Read => not Is_Assignment_LHS,
Write => Is_Assignment_LHS);
if Needs_Variable_Reference_Marker
(N => N,
Calls_OK => False)
then
declare
Is_Assignment_LHS : constant Boolean := Is_LHS (N) = Yes;
begin
Build_Variable_Reference_Marker
(N => N,
Read => not Is_Assignment_LHS,
Write => Is_Assignment_LHS);
end;
end if;
end Find_Direct_Name;
@ -6047,8 +6053,7 @@ package body Sem_Ch8 is
-- Local variables
Is_Assignment_LHS : constant Boolean := Is_LHS (N) = Yes;
Selector : constant Node_Id := Selector_Name (N);
Selector : constant Node_Id := Selector_Name (N);
Candidate : Entity_Id := Empty;
P_Name : Entity_Id;
@ -6621,11 +6626,19 @@ package body Sem_Ch8 is
-- reference is a write when it appears on the left hand side of an
-- assignment.
if not Within_Subprogram_Call (N) then
Build_Variable_Reference_Marker
(N => N,
Read => not Is_Assignment_LHS,
Write => Is_Assignment_LHS);
if Needs_Variable_Reference_Marker
(N => N,
Calls_OK => False)
then
declare
Is_Assignment_LHS : constant Boolean := Is_LHS (N) = Yes;
begin
Build_Variable_Reference_Marker
(N => N,
Read => not Is_Assignment_LHS,
Write => Is_Assignment_LHS);
end;
end if;
end Find_Expanded_Name;
@ -8301,7 +8314,6 @@ package body Sem_Ch8 is
----------------------
procedure Mark_Use_Clauses (Id : Node_Or_Entity_Id) is
procedure Mark_Parameters (Call : Entity_Id);
-- Perform use_type_clause marking for all parameters in a subprogram
-- or operator call.

View File

@ -2072,8 +2072,8 @@ package body Sem_Elab is
if Legacy_Elaboration_Checks then
return;
-- Nothing to do for ASIS. As a result, ABE checks and diagnostics are
-- not performed in this mode.
-- Nothing to do for ASIS because ABE checks and diagnostics are not
-- performed in this mode.
elsif ASIS_Mode then
return;
@ -2274,166 +2274,16 @@ package body Sem_Elab is
Read : Boolean;
Write : Boolean)
is
function In_Compilation_Instance_Formal_Part
(Nod : Node_Id) return Boolean;
-- Determine whether arbitrary node Nod appears within the formal part
-- of an instantiation which acts as a compilation unit.
function In_Pragma (Nod : Node_Id) return Boolean;
-- Determine whether arbitrary node Nod appears within a pragma
-----------------------------------------
-- In_Compilation_Instance_Formal_Part --
-----------------------------------------
function In_Compilation_Instance_Formal_Part
(Nod : Node_Id) return Boolean
is
Par : Node_Id;
begin
Par := Nod;
while Present (Par) loop
if Nkind (Par) = N_Generic_Association
and then Nkind (Parent (Par)) in N_Generic_Instantiation
and then Nkind (Parent (Parent (Par))) = N_Compilation_Unit
then
return True;
-- Prevent the search from going too far
elsif Is_Body_Or_Package_Declaration (Par) then
exit;
end if;
Par := Parent (Par);
end loop;
return False;
end In_Compilation_Instance_Formal_Part;
---------------
-- In_Pragma --
---------------
function In_Pragma (Nod : Node_Id) return Boolean is
Par : Node_Id;
begin
Par := Nod;
while Present (Par) loop
if Nkind (Par) = N_Pragma then
return True;
-- Prevent the search from going too far
elsif Is_Body_Or_Package_Declaration (Par) then
exit;
end if;
Par := Parent (Par);
end loop;
return False;
end In_Pragma;
-- Local variables
Marker : Node_Id;
Prag : Node_Id;
Var_Attrs : Variable_Attributes;
Var_Id : Entity_Id;
-- Start of processing for Build_Variable_Reference_Marker
begin
-- Nothing to do when switch -gnatH (legacy elaboration checking mode
-- enabled) is in effect because the legacy ABE mechanism does not need
-- to carry out this action.
if Legacy_Elaboration_Checks then
return;
-- Nothing to do for ASIS. As a result, ABE checks and diagnostics are
-- not performed in this mode.
elsif ASIS_Mode then
return;
-- Nothing to do when the reference is being preanalyzed as the marker
-- will be inserted in the wrong place.
elsif Preanalysis_Active then
return;
-- Nothing to do when the input does not denote a reference
elsif not Nkind_In (N, N_Expanded_Name, N_Identifier) then
return;
-- Nothing to do for internally-generated references
elsif not Comes_From_Source (N) then
return;
-- Nothing to do when the reference is erroneous, left in a bad state,
-- or does not denote a variable.
elsif not (Present (Entity (N))
and then Ekind (Entity (N)) = E_Variable
and then Entity (N) /= Any_Id)
then
return;
-- Nothing to do when the reference appears within the formal part of
-- an instantiation which acts as compilation unit because there is no
-- proper context for the insertion of the marker.
-- Performance note: parent traversal
elsif In_Compilation_Instance_Formal_Part (N) then
return;
end if;
Extract_Variable_Reference_Attributes
(Ref => N,
Var_Id => Var_Id,
Attrs => Var_Attrs);
Prag := SPARK_Pragma (Var_Id);
if Comes_From_Source (Var_Id)
-- Both the variable and the reference must appear in SPARK_Mode On
-- regions because this scenario falls under the SPARK rules.
and then Present (Prag)
and then Get_SPARK_Mode_From_Annotation (Prag) = On
and then Is_SPARK_Mode_On_Node (N)
-- The reference must not be considered when it appears in a pragma.
-- If the pragma has run-time semantics, then the reference will be
-- reconsidered once the pragma is expanded.
-- Performance note: parent traversal
and then not In_Pragma (N)
then
null;
-- Otherwise the reference is not suitable for ABE processing. This
-- prevents the generation of variable markers which will never play
-- a role in ABE diagnostics.
else
return;
end if;
-- At this point it is known that the variable reference will play some
-- role in ABE checks and diagnostics. Create a corresponding variable
-- marker in case the original variable reference is folded or optimized
-- away.
Marker := Make_Variable_Reference_Marker (Sloc (N));
-- Inherit the attributes of the original variable reference
@ -2469,8 +2319,8 @@ package body Sem_Elab is
if Legacy_Elaboration_Checks then
return;
-- Nothing to do for ASIS. As a result, no ABE checks and diagnostics
-- are performed in this mode.
-- Nothing to do for ASIS because ABE checks and diagnostics are not
-- performed in this mode.
elsif ASIS_Mode then
return;
@ -10860,8 +10710,8 @@ package body Sem_Elab is
if Legacy_Elaboration_Checks then
return;
-- Nothing to do for ASIS. As a result, no ABE checks and diagnostics
-- are performed in this mode.
-- Nothing to do for ASIS because ABE checks and diagnostics are not
-- performed in this mode.
elsif ASIS_Mode then
return;

View File

@ -3667,10 +3667,15 @@ package body Sem_Res is
-- read IN, IN OUT
-- write IN OUT, OUT
Build_Variable_Reference_Marker
(N => A,
Read => Ekind (F) /= E_Out_Parameter,
Write => Ekind (F) /= E_In_Parameter);
if Needs_Variable_Reference_Marker
(N => A,
Calls_OK => True)
then
Build_Variable_Reference_Marker
(N => A,
Read => Ekind (F) /= E_Out_Parameter,
Write => Ekind (F) /= E_In_Parameter);
end if;
Orig_A := Entity (A);

View File

@ -19247,6 +19247,144 @@ package body Sem_Util is
end if;
end Needs_Simple_Initialization;
-------------------------------------
-- Needs_Variable_Reference_Marker --
-------------------------------------
function Needs_Variable_Reference_Marker
(N : Node_Id;
Calls_OK : Boolean) return Boolean
is
function Within_Suitable_Context (Ref : Node_Id) return Boolean;
-- Deteremine whether variable reference Ref appears within a suitable
-- context that allows the creation of a marker.
-----------------------------
-- Within_Suitable_Context --
-----------------------------
function Within_Suitable_Context (Ref : Node_Id) return Boolean is
Par : Node_Id;
begin
Par := Ref;
while Present (Par) loop
-- The context is not suitable when the reference appears within
-- the formal part of an instantiation which acts as compilation
-- unit because there is no proper list for the insertion of the
-- marker.
if Nkind (Par) = N_Generic_Association
and then Nkind (Parent (Par)) in N_Generic_Instantiation
and then Nkind (Parent (Parent (Par))) = N_Compilation_Unit
then
return False;
-- The context is not suitable when the reference appears within
-- a pragma. If the pragma has run-time semantics, the reference
-- will be reconsidered once the pragma is expanded.
elsif Nkind (Par) = N_Pragma then
return False;
-- The context is not suitable when the reference appears within a
-- subprogram call, and the caller requests this behavior.
elsif not Calls_OK
and then Nkind_In (Par, N_Entry_Call_Statement,
N_Function_Call,
N_Procedure_Call_Statement)
then
return False;
-- Prevent the search from going too far
elsif Is_Body_Or_Package_Declaration (Par) then
exit;
end if;
Par := Parent (Par);
end loop;
return True;
end Within_Suitable_Context;
-- Local variables
Prag : Node_Id;
Var_Id : Entity_Id;
-- Start of processing for Needs_Variable_Reference_Marker
begin
-- No marker needs to be created when switch -gnatH (legacy elaboration
-- checking mode enabled) is in effect because the legacy ABE mechanism
-- does use markers.
if Legacy_Elaboration_Checks then
return False;
-- No marker needs to be created for ASIS because ABE diagnostics and
-- checks are not performed in this mode.
elsif ASIS_Mode then
return False;
-- No marker needs to be created when the reference is preanalyzed
-- because the marker will be inserted in the wrong place.
elsif Preanalysis_Active then
return False;
-- Only references warrant a marker
elsif not Nkind_In (N, N_Expanded_Name, N_Identifier) then
return False;
-- Only source references warrant a marker
elsif not Comes_From_Source (N) then
return False;
-- No marker needs to be created when the reference is erroneous, left
-- in a bad state, or does not denote a variable.
elsif not (Present (Entity (N))
and then Ekind (Entity (N)) = E_Variable
and then Entity (N) /= Any_Id)
then
return False;
end if;
Var_Id := Entity (N);
Prag := SPARK_Pragma (Var_Id);
-- Both the variable and reference must appear in SPARK_Mode On regions
-- because this elaboration scenario falls under the SPARK rules.
if not (Comes_From_Source (Var_Id)
and then Present (Prag)
and then Get_SPARK_Mode_From_Annotation (Prag) = On
and then Is_SPARK_Mode_On_Node (N))
then
return False;
-- No marker needs to be created when the reference does not appear
-- within a suitable context (see body for details).
-- Performance note: parent traversal
elsif not Within_Suitable_Context (N) then
return False;
end if;
-- At this point it is known that the variable reference will play a
-- role in ABE diagnostics and requires a marker.
return True;
end Needs_Variable_Reference_Marker;
------------------------
-- New_Copy_List_Tree --
------------------------

View File

@ -2224,6 +2224,13 @@ package Sem_Util is
-- set to False, but if Consider_IS is set to True, then the cases above
-- mentioning Normalize_Scalars also apply for Initialize_Scalars mode.
function Needs_Variable_Reference_Marker
(N : Node_Id;
Calls_OK : Boolean) return Boolean;
-- Determine whether arbitrary node N denotes a reference to a variable
-- which is suitable for SPARK elaboration checks. Flag Calls_OK should
-- be set when the reference is allowed to appear within calls.
function New_Copy_List_Tree (List : List_Id) return List_Id;
-- Copy recursively an analyzed list of nodes. Uses New_Copy_Tree defined
-- below. As for New_Copy_Tree, it is illegal to attempt to copy extended