[Ada] Performance degradation with references

Message ID 20180611092133.GA134814@adacore.com
State New
Headers show
Series
  • [Ada] Performance degradation with references
Related show

Commit Message

Pierre-Marie de Rodat June 11, 2018, 9:21 a.m.
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.

Tested on x86_64-pc-linux-gnu, committed on trunk

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.

Patch

--- gcc/ada/sem_ch8.adb
+++ 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.

--- gcc/ada/sem_elab.adb
+++ 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;

--- gcc/ada/sem_res.adb
+++ 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);
 

--- gcc/ada/sem_util.adb
+++ 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 --
    ------------------------

--- gcc/ada/sem_util.ads
+++ 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