This patch improves the performance of the new ABE mechanism by eliminating multiple traversals of the same subprogram body by memoizing all the nested scenarios found within.
Tested on x86_64-pc-linux-gnu, committed on trunk 2017-11-08 Hristian Kirtchev <kirtc...@adacore.com> * einfo.adb: Elist36 is now used as Nested_Scenarios. (Nested_Scenarios): New routine. (Set_Nested_Scenarios): New routine. (Write_Field36_Name): New routine. * einfo.ads: Add new attribute Nested_Scenarios along with occurrences in entities. (Nested_Scenarios): New routine along with pragma Inline. (Set_Nested_Scenarios): New routine along with pragma Inline. * sem_elab.adb (Find_And_Process_Nested_Scenarios): New routine. (Process_Nested_Scenarios): New routine. (Traverse_Body): When a subprogram body is traversed for the first time, find, save, and process all suitable scenarios found within. Subsequent traversals of the same subprogram body utilize the saved scenarios.
Index: einfo.adb =================================================================== --- einfo.adb (revision 254523) +++ einfo.adb (working copy) @@ -273,6 +273,7 @@ -- Entry_Max_Queue_Lengths_Array Node35 -- Import_Pragma Node35 + -- Nested_Scenarios Elist36 -- Validated_Object Node36 -- Class_Wide_Clone Node38 @@ -2867,6 +2868,14 @@ return Flag22 (Id); end Needs_No_Actuals; + function Nested_Scenarios (Id : E) return L is + begin + pragma Assert (Ekind_In (Id, E_Function, + E_Procedure, + E_Subprogram_Body)); + return Elist36 (Id); + end Nested_Scenarios; + function Never_Set_In_Source (Id : E) return B is begin return Flag115 (Id); @@ -6071,6 +6080,14 @@ Set_Flag22 (Id, V); end Set_Needs_No_Actuals; + procedure Set_Nested_Scenarios (Id : E; V : L) is + begin + pragma Assert (Ekind_In (Id, E_Function, + E_Procedure, + E_Subprogram_Body)); + Set_Elist36 (Id, V); + end Set_Nested_Scenarios; + procedure Set_Never_Set_In_Source (Id : E; V : B := True) is begin Set_Flag115 (Id, V); @@ -11118,6 +11135,12 @@ procedure Write_Field36_Name (Id : Entity_Id) is begin case Ekind (Id) is + when E_Function + | E_Procedure + | E_Subprogram_Body + => + Write_Str ("Nested_Scenarios"); + when E_Variable => Write_Str ("Validated_Object"); Index: einfo.ads =================================================================== --- einfo.ads (revision 254523) +++ einfo.ads (working copy) @@ -3531,6 +3531,14 @@ -- interpreted as an indexing of the result of the call. It is also -- used to resolve various cases of entry calls. +-- Nested_Scenarios (Elist36) +-- Present in [stand alone] subprogram bodies. The list contains all +-- nested scenarios (see the terminology in Sem_Elab) which appear within +-- the declarations, statements, and exception handlers of the subprogram +-- body. The list improves the performance of the ABE Processing phase by +-- avoiding a full tree traversal when the same subprogram body is part +-- of several distinct paths in the elaboration graph. + -- Never_Set_In_Source (Flag115) -- Defined in all entities, but can be set only for variables and -- parameters. This flag is set if the object is never assigned a value @@ -6076,6 +6084,7 @@ -- Linker_Section_Pragma (Node33) -- Contract (Node34) -- Import_Pragma (Node35) (non-generic case only) + -- Nested_Scenarios (Elist36) -- Class_Wide_Clone (Node38) -- Protected_Subprogram (Node39) (non-generic case only) -- SPARK_Pragma (Node40) @@ -6398,6 +6407,7 @@ -- Linker_Section_Pragma (Node33) -- Contract (Node34) -- Import_Pragma (Node35) (non-generic case only) + -- Nested_Scenarios (Elist36) -- Class_Wide_Clone (Node38) -- Protected_Subprogram (Node39) (non-generic case only) -- SPARK_Pragma (Node40) @@ -6592,6 +6602,7 @@ -- Extra_Formals (Node28) -- Anonymous_Masters (Elist29) -- Contract (Node34) + -- Nested_Scenarios (Elist36) -- SPARK_Pragma (Node40) -- Contains_Ignored_Ghost_Code (Flag279) -- SPARK_Pragma_Inherited (Flag265) @@ -7308,6 +7319,7 @@ function Must_Have_Preelab_Init (Id : E) return B; function Needs_Debug_Info (Id : E) return B; function Needs_No_Actuals (Id : E) return B; + function Nested_Scenarios (Id : E) return L; function Never_Set_In_Source (Id : E) return B; function Next_Inlined_Subprogram (Id : E) return E; function No_Dynamic_Predicate_On_Actual (Id : E) return B; @@ -8005,6 +8017,7 @@ procedure Set_Must_Have_Preelab_Init (Id : E; V : B := True); procedure Set_Needs_Debug_Info (Id : E; V : B := True); procedure Set_Needs_No_Actuals (Id : E; V : B := True); + procedure Set_Nested_Scenarios (Id : E; V : L); procedure Set_Never_Set_In_Source (Id : E; V : B := True); procedure Set_Next_Inlined_Subprogram (Id : E; V : E); procedure Set_No_Dynamic_Predicate_On_Actual (Id : E; V : B := True); @@ -8857,6 +8870,7 @@ pragma Inline (Must_Have_Preelab_Init); pragma Inline (Needs_Debug_Info); pragma Inline (Needs_No_Actuals); + pragma Inline (Nested_Scenarios); pragma Inline (Never_Set_In_Source); pragma Inline (Next_Index); pragma Inline (Next_Inlined_Subprogram); @@ -9343,6 +9357,7 @@ pragma Inline (Set_Must_Have_Preelab_Init); pragma Inline (Set_Needs_Debug_Info); pragma Inline (Set_Needs_No_Actuals); + pragma Inline (Set_Nested_Scenarios); pragma Inline (Set_Never_Set_In_Source); pragma Inline (Set_Next_Inlined_Subprogram); pragma Inline (Set_No_Dynamic_Predicate_On_Actual); Index: sem_elab.adb =================================================================== --- sem_elab.adb (revision 254531) +++ sem_elab.adb (working copy) @@ -26,6 +26,7 @@ with Atree; use Atree; with Debug; use Debug; with Einfo; use Einfo; +with Elists; use Elists; with Errout; use Errout; with Exp_Ch11; use Exp_Ch11; with Exp_Tss; use Exp_Tss; @@ -8502,85 +8503,173 @@ In_Partial_Fin : Boolean; In_Task_Body : Boolean) is - function Is_Potential_Scenario (Nod : Node_Id) return Traverse_Result; - -- Determine whether arbitrary node Nod denotes a suitable scenario and - -- if so, process it. + procedure Find_And_Process_Nested_Scenarios; + pragma Inline (Find_And_Process_Nested_Scenarios); + -- Examine the declarations and statements of subprogram body N for + -- suitable scenarios. Save each discovered scenario and process it + -- accordingly. - procedure Traverse_Potential_Scenarios is - new Traverse_Proc (Is_Potential_Scenario); + procedure Process_Nested_Scenarios (Nested : Elist_Id); + pragma Inline (Process_Nested_Scenarios); + -- Invoke Process_Scenario on each individual scenario whith appears in + -- list Nested. - procedure Traverse_List (List : List_Id); - -- Inspect list List for suitable elaboration scenarios and process them + --------------------------------------- + -- Find_And_Process_Nested_Scenarios -- + --------------------------------------- - --------------------------- - -- Is_Potential_Scenario -- - --------------------------- + procedure Find_And_Process_Nested_Scenarios is + Body_Id : constant Entity_Id := Defining_Entity (N); - function Is_Potential_Scenario (Nod : Node_Id) return Traverse_Result is - begin - -- Special cases + function Is_Potential_Scenario + (Nod : Node_Id) return Traverse_Result; + -- Determine whether arbitrary node Nod denotes a suitable scenario. + -- If it does, save it in the Nested_Scenarios list of the subprogram + -- body, and process it. - -- Skip constructs which do not have elaboration of their own and - -- need to be elaborated by other means such as invocation, task - -- activation, etc. + procedure Save_Scenario (Nod : Node_Id); + pragma Inline (Save_Scenario); + -- Save scenario Nod in the Nested_Scenarios list of the subprogram + -- body. - if Is_Non_Library_Level_Encapsulator (Nod) then - return Skip; + procedure Traverse_List (List : List_Id); + pragma Inline (Traverse_List); + -- Invoke Traverse_Potential_Scenarios on each node in list List - -- Terminate the traversal of a task body with an accept statement - -- when no entry calls in elaboration are allowed because the task - -- will block at run-time and none of the remaining statements will - -- be executed. + procedure Traverse_Potential_Scenarios is + new Traverse_Proc (Is_Potential_Scenario); - elsif Nkind_In (Original_Node (Nod), N_Accept_Statement, - N_Selective_Accept) - and then Restriction_Active (No_Entry_Calls_In_Elaboration_Code) - then - return Abandon; + --------------------------- + -- Is_Potential_Scenario -- + --------------------------- - -- Certain nodes carry semantic lists which act as repositories until - -- expansion transforms the node and relocates the contents. Examine - -- these lists in case expansion is disabled. + function Is_Potential_Scenario + (Nod : Node_Id) return Traverse_Result + is + begin + -- Special cases - elsif Nkind_In (Nod, N_And_Then, N_Or_Else) then - Traverse_List (Actions (Nod)); + -- Skip constructs which do not have elaboration of their own and + -- need to be elaborated by other means such as invocation, task + -- activation, etc. - elsif Nkind_In (Nod, N_Elsif_Part, N_Iteration_Scheme) then - Traverse_List (Condition_Actions (Nod)); + if Is_Non_Library_Level_Encapsulator (Nod) then + return Skip; - elsif Nkind (Nod) = N_If_Expression then - Traverse_List (Then_Actions (Nod)); - Traverse_List (Else_Actions (Nod)); + -- Terminate the traversal of a task body with an accept statement + -- when no entry calls in elaboration are allowed because the task + -- will block at run-time and the remaining statements will not be + -- executed. - elsif Nkind_In (Nod, N_Component_Association, - N_Iterated_Component_Association) - then - Traverse_List (Loop_Actions (Nod)); + elsif Nkind_In (Original_Node (Nod), N_Accept_Statement, + N_Selective_Accept) + and then Restriction_Active (No_Entry_Calls_In_Elaboration_Code) + then + return Abandon; - -- General case + -- Certain nodes carry semantic lists which act as repositories + -- until expansion transforms the node and relocates the contents. + -- Examine these lists in case expansion is disabled. - elsif Is_Suitable_Scenario (Nod) then - Process_Scenario (Nod, In_Partial_Fin, In_Task_Body); - end if; + elsif Nkind_In (Nod, N_And_Then, N_Or_Else) then + Traverse_List (Actions (Nod)); - return OK; - end Is_Potential_Scenario; + elsif Nkind_In (Nod, N_Elsif_Part, N_Iteration_Scheme) then + Traverse_List (Condition_Actions (Nod)); - ------------------- - -- Traverse_List -- - ------------------- + elsif Nkind (Nod) = N_If_Expression then + Traverse_List (Then_Actions (Nod)); + Traverse_List (Else_Actions (Nod)); - procedure Traverse_List (List : List_Id) is - Item : Node_Id; + elsif Nkind_In (Nod, N_Component_Association, + N_Iterated_Component_Association) + then + Traverse_List (Loop_Actions (Nod)); + -- General case + + -- Save a suitable scenario in the Nested_Scenarios list of the + -- subprogram body. As a result any subsequent traversals of the + -- subprogram body started from a different top level scenario no + -- longer need to reexamine the tree. + + elsif Is_Suitable_Scenario (Nod) then + Save_Scenario (Nod); + Process_Scenario (Nod, In_Partial_Fin, In_Task_Body); + end if; + + return OK; + end Is_Potential_Scenario; + + ------------------- + -- Save_Scenario -- + ------------------- + + procedure Save_Scenario (Nod : Node_Id) is + Nested : Elist_Id; + + begin + Nested := Nested_Scenarios (Body_Id); + + if No (Nested) then + Nested := New_Elmt_List; + Set_Nested_Scenarios (Body_Id, Nested); + end if; + + Append_Elmt (Nod, Nested); + end Save_Scenario; + + ------------------- + -- Traverse_List -- + ------------------- + + procedure Traverse_List (List : List_Id) is + Item : Node_Id; + + begin + Item := First (List); + while Present (Item) loop + Traverse_Potential_Scenarios (Item); + Next (Item); + end loop; + end Traverse_List; + + -- Start of processing for Find_And_Process_Nested_Scenarios + begin - Item := First (List); - while Present (Item) loop - Traverse_Potential_Scenarios (Item); - Next (Item); + -- Examine the declarations for suitable scenarios + + Traverse_List (Declarations (N)); + + -- Examine the handled sequence of statements. This also includes any + -- exceptions handlers. + + Traverse_Potential_Scenarios (Handled_Statement_Sequence (N)); + end Find_And_Process_Nested_Scenarios; + + ------------------------------ + -- Process_Nested_Scenarios -- + ------------------------------ + + procedure Process_Nested_Scenarios (Nested : Elist_Id) is + Nested_Elmt : Elmt_Id; + + begin + Nested_Elmt := First_Elmt (Nested); + while Present (Nested_Elmt) loop + Process_Scenario + (N => Node (Nested_Elmt), + In_Partial_Fin => In_Partial_Fin, + In_Task_Body => In_Task_Body); + + Next_Elmt (Nested_Elmt); end loop; - end Traverse_List; + end Process_Nested_Scenarios; + -- Local variables + + Nested : Elist_Id; + -- Start of processing for Traverse_Body begin @@ -8605,14 +8694,23 @@ Visited_Bodies.Set (N, True); end if; - -- Examine the declarations for suitable scenarios + Nested := Nested_Scenarios (Defining_Entity (N)); - Traverse_List (Declarations (N)); + -- The subprogram body was already examined as part of the elaboration + -- graph starting from a different top level scenario. There is no need + -- to traverse the declarations and statements again because this will + -- yield the exact same scenarios. Use the nested scenarios collected + -- during the first inspection of the body. - -- Examine the handled sequence of statements. This also includes any - -- exceptions handlers. + if Present (Nested) then + Process_Nested_Scenarios (Nested); - Traverse_Potential_Scenarios (Handled_Statement_Sequence (N)); + -- Otherwise examine the declarations and statements of the subprogram + -- body for suitable scenarios, save and process them accordingly. + + else + Find_And_Process_Nested_Scenarios; + end if; end Traverse_Body; ---------------------------------