diff mbox

[Ada] Expression functions need not trigger loading of package body

Message ID 20160427110153.GA54961@adacore.com
State New
Headers show

Commit Message

Arnaud Charlet April 27, 2016, 11:01 a.m. UTC
The expression functions introduced in Ada 2012 implicitly come with the
Inline aspect in GNAT.  And, for inter-unit inlining, they were handled by
the inlining machinery as any other inlined subprograms, which means that
they were causing the package body (if any) to be loaded and analyzed.

That's both unnecessary and inefficient, so this patch corrects it as well
as streamlines the implementation of the main entry point for inlining.

The change can be exhibited by the now quiet compilation of Client in:

with P;

procedure Client is

   X : Boolean := P.Foo;

begin
   null;
end Client;
package P is

   function Foo return Boolean is (True);

   procedure Other;

end P;
package body P is

   procedure Other is
   begin
      Unrelated;
   end Other;

end P;

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

2016-04-27  Eric Botcazou  <ebotcazou@adacore.com>

	* inline.adb (Add_Inlined_Body): Overhaul implementation,
	robustify handling of -gnatn1, add special treatment for
	expression functions.
diff mbox

Patch

Index: inline.adb
===================================================================
--- inline.adb	(revision 235481)
+++ inline.adb	(working copy)
@@ -6,7 +6,7 @@ 
 --                                                                          --
 --                                 B o d y                                  --
 --                                                                          --
---          Copyright (C) 1992-2015, Free Software Foundation, Inc.         --
+--          Copyright (C) 1992-2016, Free Software Foundation, Inc.         --
 --                                                                          --
 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
 -- terms of the  GNU General Public License as published  by the Free Soft- --
@@ -390,6 +390,40 @@ 
          return;
       end if;
 
+      --  Find out whether the call must be inlined. Unless the result is
+      --  Dont_Inline, Must_Inline also creates an edge for the call in the
+      --  callgraph; however, it will not be activated until after Is_Called
+      --  is set on the subprogram.
+
+      Level := Must_Inline;
+
+      if Level = Dont_Inline then
+         return;
+      end if;
+
+      --  If the call was generated by the compiler and is to a subprogram in
+      --  a run-time unit, we need to suppress debugging information for it,
+      --  so that the code that is eventually inlined will not affect the
+      --  debugging of the program. We do not do it if the call comes from
+      --  source because, even if the call is inlined, the user may expect it
+      --  to be present in the debugging information.
+
+      if not Comes_From_Source (N)
+        and then In_Extended_Main_Source_Unit (N)
+        and then
+          Is_Predefined_File_Name (Unit_File_Name (Get_Source_Unit (E)))
+      then
+         Set_Needs_Debug_Info (E, False);
+      end if;
+
+      --  If the subprogram is an expression function, then there is no need to
+      --  load any package body since the body of the function is in the spec.
+
+      if Is_Expression_Function (E) then
+         Set_Is_Called (E);
+         return;
+      end if;
+
       --  Find unit containing E, and add to list of inlined bodies if needed.
       --  If the body is already present, no need to load any other unit. This
       --  is the case for an initialization procedure, which appears in the
@@ -403,77 +437,48 @@ 
       --  no enclosing package to retrieve. In this case, it is the body of
       --  the function that will have to be loaded.
 
-      Level := Must_Inline;
+      declare
+         Pack : constant Entity_Id := Get_Code_Unit_Entity (E);
 
-      if Level /= Dont_Inline then
-         declare
-            Pack : constant Entity_Id := Get_Code_Unit_Entity (E);
+      begin
+         if Pack = E then
+            Set_Is_Called (E);
+            Inlined_Bodies.Increment_Last;
+            Inlined_Bodies.Table (Inlined_Bodies.Last) := E;
 
-         begin
-            --  Ensure that Analyze_Inlined_Bodies will be invoked after
-            --  completing the analysis of the current unit.
+         elsif Ekind (Pack) = E_Package then
+            Set_Is_Called (E);
 
-            Inline_Processing_Required := True;
+            if Is_Generic_Instance (Pack) then
+               null;
 
-            if Pack = E then
+            --  Do not inline the package if the subprogram is an init proc
+            --  or other internally generated subprogram, because in that
+            --  case the subprogram body appears in the same unit that
+            --  declares the type, and that body is visible to the back end.
+            --  Do not inline it either if it is in the main unit.
+            --  Extend the -gnatn2 processing to -gnatn1 for Inline_Always
+            --  calls if the back-end takes care of inlining the call.
 
-               --  Library-level inlined function. Add function itself to
-               --  list of needed units.
-
-               Set_Is_Called (E);
+            elsif (Level = Inline_Package
+                    or else (Level = Inline_Call
+                              and then Has_Pragma_Inline_Always (E)
+                              and then Back_End_Inlining))
+              and then not Is_Inlined (Pack)
+              and then not Is_Internal (E)
+              and then not In_Main_Unit_Or_Subunit (Pack)
+            then
+               Set_Is_Inlined (Pack);
                Inlined_Bodies.Increment_Last;
-               Inlined_Bodies.Table (Inlined_Bodies.Last) := E;
-
-            elsif Ekind (Pack) = E_Package then
-               Set_Is_Called (E);
-
-               if Is_Generic_Instance (Pack) then
-                  null;
-
-               --  Do not inline the package if the subprogram is an init proc
-               --  or other internally generated subprogram, because in that
-               --  case the subprogram body appears in the same unit that
-               --  declares the type, and that body is visible to the back end.
-               --  Do not inline it either if it is in the main unit.
-
-               elsif Level = Inline_Package
-                 and then not Is_Inlined (Pack)
-                 and then not Is_Internal (E)
-                 and then not In_Main_Unit_Or_Subunit (Pack)
-               then
-                  Set_Is_Inlined (Pack);
-                  Inlined_Bodies.Increment_Last;
-                  Inlined_Bodies.Table (Inlined_Bodies.Last) := Pack;
-
-               --  Extend the -gnatn2 processing to -gnatn1 for Inline_Always
-               --  calls if the back-end takes care of inlining the call.
-
-               elsif Level = Inline_Call
-                 and then Has_Pragma_Inline_Always (E)
-                 and then Back_End_Inlining
-               then
-                  Set_Is_Inlined (Pack);
-                  Inlined_Bodies.Increment_Last;
-                  Inlined_Bodies.Table (Inlined_Bodies.Last) := Pack;
-               end if;
+               Inlined_Bodies.Table (Inlined_Bodies.Last) := Pack;
             end if;
+         end if;
 
-            --  If the call was generated by the compiler and is to a function
-            --  in a run-time unit, we need to suppress debugging information
-            --  for it, so that the code that is eventually inlined will not
-            --  affect debugging of the program. We do not do it if the call
-            --  comes from source because, even if the call is inlined, the
-            --  user may expect it to be present in the debugging information.
+         --  Ensure that Analyze_Inlined_Bodies will be invoked after
+         --  completing the analysis of the current unit.
 
-            if not Comes_From_Source (N)
-               and then In_Extended_Main_Source_Unit (N)
-               and then
-                 Is_Predefined_File_Name (Unit_File_Name (Get_Source_Unit (E)))
-            then
-               Set_Needs_Debug_Info (E, False);
-            end if;
-         end;
-      end if;
+         Inline_Processing_Required := True;
+      end;
    end Add_Inlined_Body;
 
    ----------------------------