diff mbox series

[Ada] Improve the performance of the new ABE mechanism

Message ID 20171108151828.GA102879@adacore.com
State New
Headers show
Series [Ada] Improve the performance of the new ABE mechanism | expand

Commit Message

Pierre-Marie de Rodat Nov. 8, 2017, 3:18 p.m. UTC
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  <kirtchev@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.
diff mbox series

Patch

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;
 
    ---------------------------------