From 692918a9c1e601ecf81f99eb4926268964f62181 Mon Sep 17 00:00:00 2001 From: Hristian Kirtchev Date: Mon, 11 Jun 2018 09:16:54 +0000 Subject: [PATCH] [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 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 --- gcc/ada/ChangeLog | 19 +++++ gcc/ada/sem_ch8.adb | 42 +++++++---- gcc/ada/sem_elab.adb | 162 ++----------------------------------------- gcc/ada/sem_res.adb | 13 ++-- gcc/ada/sem_util.adb | 138 ++++++++++++++++++++++++++++++++++++ gcc/ada/sem_util.ads | 7 ++ 6 files changed, 206 insertions(+), 175 deletions(-) diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog index 53d07c6c167..0f51c439308 100644 --- a/gcc/ada/ChangeLog +++ b/gcc/ada/ChangeLog @@ -1,3 +1,22 @@ +2018-06-11 Hristian Kirtchev + + * 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 * doc/gnat_rm.rst, doc/gnat_ugn.rst: Rename "GPL Edition" into diff --git a/gcc/ada/sem_ch8.adb b/gcc/ada/sem_ch8.adb index 02471d73bbb..830aa038b21 100644 --- a/gcc/ada/sem_ch8.adb +++ b/gcc/ada/sem_ch8.adb @@ -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. diff --git a/gcc/ada/sem_elab.adb b/gcc/ada/sem_elab.adb index 72bb0cbc46e..c2fc7c5977b 100644 --- a/gcc/ada/sem_elab.adb +++ b/gcc/ada/sem_elab.adb @@ -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; diff --git a/gcc/ada/sem_res.adb b/gcc/ada/sem_res.adb index e569cc88485..e162e78a66e 100644 --- a/gcc/ada/sem_res.adb +++ b/gcc/ada/sem_res.adb @@ -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); diff --git a/gcc/ada/sem_util.adb b/gcc/ada/sem_util.adb index 8fbad1d7e87..9ac04c3afda 100644 --- a/gcc/ada/sem_util.adb +++ b/gcc/ada/sem_util.adb @@ -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 -- ------------------------ diff --git a/gcc/ada/sem_util.ads b/gcc/ada/sem_util.ads index a2eca15b257..a0b95fa1cbc 100644 --- a/gcc/ada/sem_util.ads +++ b/gcc/ada/sem_util.ads @@ -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