Commit 4b96d386 by Eric Botcazou Committed by Pierre-Marie de Rodat

[Ada] Compiler speedup with inlining across units

This change is aimed at speeding up the inlining across units done by
the Ada compiler when -gnatn is specified and in the presence of units
instantiating a lot of generic packages.

The current implementation is as follows: when a generic package is
being instantiated, the compiler scans its spec for the presence of
subprograms with an aspect/pragma Inline and, upon finding one,
schedules the instantiation of its body.  That's not very efficient
because the compiler doesn't know yet if one of those inlined
subprograms will eventually be called from the main unit.

The new implementation arranges for the compiler to instantiate the body
on demand, i.e. when it encounters a call to one of the inlined
subprograms.  That's still not optimal because, at this point, the
compiler has not yet computed whether the call itself is reachable from
the main unit (it will do this computation at the very end of the
processing, just before sending the inlined units to the code generator)
but that's nevertheless a net progress.

The patch also enhances the -gnatd.j option to make it output the list
of instances "inlined" this way.  The following package is a simple
example:

with Q;

procedure P is
begin
  Q.Proc;
end;

package Q is

  procedure Proc;
  pragma Inline (Proc);

end Q;

with G;

package body Q is

  package My_G is new G (1);

  procedure Proc is
    Val : constant Integer := My_G.Func;
  begin
    if Val /= 1 then
      raise Program_Error;
    end if;
  end;

end Q;

generic

  Value : Integer;

package G is

  function Func return Integer;
  pragma Inline (Func);

end G;

package body G is

  function Func return Integer is
  begin
    return Value;
  end;

end G;

2019-08-14  Eric Botcazou  <ebotcazou@adacore.com>

gcc/ada/

	* einfo.ads (Is_Called): Document new usage on E_Package
	entities.
	* einfo.adb (Is_Called): Accept E_Package entities.
	(Set_Is_Called): Likewise.
	* exp_ch6.adb (Expand_Call_Helper): Move code dealing with
	instances for back-end inlining to Add_Inlined_Body.
	* inline.ads: Remove with clauses for Alloc and Table.
	(Pending_Instantiations): Move to...
	* inline.adb: Add with clauses for Alloc, Uintp, Table and
	GNAT.HTable.
	(Backend_Instances): New variable.
	(Pending_Instantiations): ...here.
	(Called_Pending_Instantiations): New table.
	(Node_Table_Size): New constant.
	(Node_Header_Num): New subtype.
	(Node_Hash): New function.
	(To_Pending_Instantiations): New hash table.
	(Add_Inlined_Body): Bail out early for subprograms in the main
	unit or subunit.  Likewise if the Is_Called flag is set.  If the
	subprogram is an instance, invoke Add_Inlined_Instance.  Call
	Set_Is_Called earlier.  If the subrogram is within an instance,
	invoke Add_Inlined_Instance.  Also deal with the case where the
	call itself is within an instance.
	(Add_Inlined_Instance): New procedure.
	(Add_Inlined_Subprogram): Remove conditions always fulfilled.
	(Add_Pending_Instantiation): Move the defence against ludicruous
	number of instantiations to here. When back-end inlining is
	enabled, associate an instantiation with its index in table and
	mark a few selected kinds of instantiations as always needed.
	(Initialize): Set Backend_Instances to No_Elist.
	(Instantiate_Body): New procedure doing the work extracted
	from...
	(Instantiate_Bodies): ...here.  When back-end inlining is
	enabled, loop over Called_Pending_Instantiations instead of
	Pending_Instantiations.
	(Is_Nested): Minor tweak.
	(List_Inlining_Info): Also list the contents of
	Backend_Instances.
	* sem_ch12.adb (Might_Inline_Subp): Return early if Is_Inlined
	is set and otherwise set it before returning true.
	(Analyze_Package_Instantiation): Remove the defence against
	ludicruous number of instantiations.  Invoke
	Remove_Dead_Instance instead of doing the removal manually if
	there is a guaranteed ABE.

From-SVN: r274465
parent 72e324b6
2019-08-14 Eric Botcazou <ebotcazou@adacore.com>
* einfo.ads (Is_Called): Document new usage on E_Package
entities.
* einfo.adb (Is_Called): Accept E_Package entities.
(Set_Is_Called): Likewise.
* exp_ch6.adb (Expand_Call_Helper): Move code dealing with
instances for back-end inlining to Add_Inlined_Body.
* inline.ads: Remove with clauses for Alloc and Table.
(Pending_Instantiations): Move to...
* inline.adb: Add with clauses for Alloc, Uintp, Table and
GNAT.HTable.
(Backend_Instances): New variable.
(Pending_Instantiations): ...here.
(Called_Pending_Instantiations): New table.
(Node_Table_Size): New constant.
(Node_Header_Num): New subtype.
(Node_Hash): New function.
(To_Pending_Instantiations): New hash table.
(Add_Inlined_Body): Bail out early for subprograms in the main
unit or subunit. Likewise if the Is_Called flag is set. If the
subprogram is an instance, invoke Add_Inlined_Instance. Call
Set_Is_Called earlier. If the subrogram is within an instance,
invoke Add_Inlined_Instance. Also deal with the case where the
call itself is within an instance.
(Add_Inlined_Instance): New procedure.
(Add_Inlined_Subprogram): Remove conditions always fulfilled.
(Add_Pending_Instantiation): Move the defence against ludicruous
number of instantiations to here. When back-end inlining is
enabled, associate an instantiation with its index in table and
mark a few selected kinds of instantiations as always needed.
(Initialize): Set Backend_Instances to No_Elist.
(Instantiate_Body): New procedure doing the work extracted
from...
(Instantiate_Bodies): ...here. When back-end inlining is
enabled, loop over Called_Pending_Instantiations instead of
Pending_Instantiations.
(Is_Nested): Minor tweak.
(List_Inlining_Info): Also list the contents of
Backend_Instances.
* sem_ch12.adb (Might_Inline_Subp): Return early if Is_Inlined
is set and otherwise set it before returning true.
(Analyze_Package_Instantiation): Remove the defence against
ludicruous number of instantiations. Invoke
Remove_Dead_Instance instead of doing the removal manually if
there is a guaranteed ABE.
2019-08-14 Gary Dismukes <dismukes@adacore.com> 2019-08-14 Gary Dismukes <dismukes@adacore.com>
* exp_ch3.adb (Predef_Spec_Or_Body): For an equality operation * exp_ch3.adb (Predef_Spec_Or_Body): For an equality operation
......
...@@ -2140,7 +2140,7 @@ package body Einfo is ...@@ -2140,7 +2140,7 @@ package body Einfo is
function Is_Called (Id : E) return B is function Is_Called (Id : E) return B is
begin begin
pragma Assert (Ekind_In (Id, E_Procedure, E_Function)); pragma Assert (Ekind_In (Id, E_Procedure, E_Function, E_Package));
return Flag102 (Id); return Flag102 (Id);
end Is_Called; end Is_Called;
...@@ -5344,7 +5344,7 @@ package body Einfo is ...@@ -5344,7 +5344,7 @@ package body Einfo is
procedure Set_Is_Called (Id : E; V : B := True) is procedure Set_Is_Called (Id : E; V : B := True) is
begin begin
pragma Assert (Ekind_In (Id, E_Procedure, E_Function)); pragma Assert (Ekind_In (Id, E_Procedure, E_Function, E_Package));
Set_Flag102 (Id, V); Set_Flag102 (Id, V);
end Set_Is_Called; end Set_Is_Called;
......
...@@ -2366,9 +2366,9 @@ package Einfo is ...@@ -2366,9 +2366,9 @@ package Einfo is
-- i.e. Standard.Boolean and all types ultimately derived from it. -- i.e. Standard.Boolean and all types ultimately derived from it.
-- Is_Called (Flag102) -- Is_Called (Flag102)
-- Defined in subprograms. Returns true if the subprogram is called -- Defined in subprograms and packages. Set if a subprogram is called
-- in the unit being compiled or in a unit in the context. Used for -- from the unit being compiled or a unit in the closure. Also set for
-- inlining. -- a package that contains called subprograms. Used only for inlining.
-- Is_Character_Type (Flag63) -- Is_Character_Type (Flag63)
-- Defined in all entities. Set for character types and subtypes, -- Defined in all entities. Set for character types and subtypes,
...@@ -6406,12 +6406,13 @@ package Einfo is ...@@ -6406,12 +6406,13 @@ package Einfo is
-- Has_Master_Entity (Flag21) -- Has_Master_Entity (Flag21)
-- Has_RACW (Flag214) (non-generic case only) -- Has_RACW (Flag214) (non-generic case only)
-- Ignore_SPARK_Mode_Pragmas (Flag301) -- Ignore_SPARK_Mode_Pragmas (Flag301)
-- In_Package_Body (Flag48) -- Is_Called (Flag102) (non-generic case only)
-- In_Use (Flag8)
-- Is_Elaboration_Checks_OK_Id (Flag148) -- Is_Elaboration_Checks_OK_Id (Flag148)
-- Is_Elaboration_Warnings_OK_Id (Flag304) -- Is_Elaboration_Warnings_OK_Id (Flag304)
-- Is_Instantiated (Flag126) -- Is_Instantiated (Flag126)
-- In_Package_Body (Flag48)
-- Is_Private_Descendant (Flag53) -- Is_Private_Descendant (Flag53)
-- In_Use (Flag8)
-- Is_Visible_Lib_Unit (Flag116) -- Is_Visible_Lib_Unit (Flag116)
-- Renamed_In_Spec (Flag231) (non-generic case only) -- Renamed_In_Spec (Flag231) (non-generic case only)
-- SPARK_Aux_Pragma_Inherited (Flag266) -- SPARK_Aux_Pragma_Inherited (Flag266)
......
...@@ -4443,62 +4443,6 @@ package body Exp_Ch6 is ...@@ -4443,62 +4443,6 @@ package body Exp_Ch6 is
or else Has_Pragma_Inline_Always (Subp) or else Has_Pragma_Inline_Always (Subp)
then then
Add_Inlined_Body (Subp, Call_Node); Add_Inlined_Body (Subp, Call_Node);
-- If the inlined call appears within an instance, then ensure
-- that the enclosing instance body is available so the back end
-- can actually perform the inlining.
if In_Instance and then Comes_From_Source (Subp) then
declare
Decl : Node_Id;
Inst : Entity_Id;
Inst_Node : Node_Id;
begin
Inst := Scope (Subp);
-- Find enclosing instance
while Present (Inst) and then Inst /= Standard_Standard loop
exit when Is_Generic_Instance (Inst);
Inst := Scope (Inst);
end loop;
if Present (Inst)
and then Is_Generic_Instance (Inst)
and then not Is_Inlined (Inst)
then
Set_Is_Inlined (Inst);
Decl := Unit_Declaration_Node (Inst);
-- Do not add a pending instantiation if the body exits
-- already, or if the instance is a compilation unit, or
-- the instance node is missing.
if Present (Corresponding_Body (Decl))
or else Nkind (Parent (Decl)) = N_Compilation_Unit
or else No (Next (Decl))
then
null;
else
-- The instantiation node usually follows the package
-- declaration for the instance. If the generic unit
-- has aspect specifications, they are transformed
-- into pragmas in the instance, and the instance node
-- appears after them.
Inst_Node := Next (Decl);
while Nkind (Inst_Node) /= N_Package_Instantiation loop
Inst_Node := Next (Inst_Node);
end loop;
Add_Pending_Instantiation (Inst_Node, Decl);
end if;
end if;
end;
end if;
end if; end if;
end if; end if;
......
...@@ -42,10 +42,8 @@ ...@@ -42,10 +42,8 @@
-- Inline_Always subprograms, but there are fewer restrictions on the source -- Inline_Always subprograms, but there are fewer restrictions on the source
-- of subprograms. -- of subprograms.
with Alloc;
with Opt; use Opt; with Opt; use Opt;
with Sem; use Sem; with Sem; use Sem;
with Table;
with Types; use Types; with Types; use Types;
with Warnsw; use Warnsw; with Warnsw; use Warnsw;
...@@ -100,14 +98,6 @@ package Inline is ...@@ -100,14 +98,6 @@ package Inline is
-- Capture values of warning flags -- Capture values of warning flags
end record; end record;
package Pending_Instantiations is new Table.Table (
Table_Component_Type => Pending_Body_Info,
Table_Index_Type => Int,
Table_Low_Bound => 0,
Table_Initial => Alloc.Pending_Instantiations_Initial,
Table_Increment => Alloc.Pending_Instantiations_Increment,
Table_Name => "Pending_Instantiations");
----------------- -----------------
-- Subprograms -- -- Subprograms --
----------------- -----------------
......
...@@ -3861,6 +3861,12 @@ package body Sem_Ch12 is ...@@ -3861,6 +3861,12 @@ package body Sem_Ch12 is
begin begin
if Inline_Processing_Required then if Inline_Processing_Required then
-- No need to recompute the answer if we know it is positive
if Is_Inlined (Gen_Unit) then
return True;
end if;
E := First_Entity (Gen_Unit); E := First_Entity (Gen_Unit);
while Present (E) loop while Present (E) loop
if Is_Subprogram (E) and then Is_Inlined (E) then if Is_Subprogram (E) and then Is_Inlined (E) then
...@@ -3870,6 +3876,7 @@ package body Sem_Ch12 is ...@@ -3870,6 +3876,7 @@ package body Sem_Ch12 is
Has_Inline_Always := True; Has_Inline_Always := True;
end if; end if;
Set_Is_Inlined (Gen_Unit);
return True; return True;
end if; end if;
...@@ -4425,17 +4432,6 @@ package body Sem_Ch12 is ...@@ -4425,17 +4432,6 @@ package body Sem_Ch12 is
end if; end if;
if Needs_Body then if Needs_Body then
-- Here is a defence against a ludicrous number of instantiations
-- caused by a circular set of instantiation attempts.
if Pending_Instantiations.Last > Maximum_Instantiations then
Error_Msg_Uint_1 := UI_From_Int (Maximum_Instantiations);
Error_Msg_N ("too many instantiations, exceeds max of^", N);
Error_Msg_N ("\limit can be changed using -gnateinn switch", N);
raise Unrecoverable_Error;
end if;
-- Indicate that the enclosing scopes contain an instantiation, -- Indicate that the enclosing scopes contain an instantiation,
-- and that cleanup actions should be delayed until after the -- and that cleanup actions should be delayed until after the
-- instance body is expanded. -- instance body is expanded.
...@@ -4633,11 +4629,10 @@ package body Sem_Ch12 is ...@@ -4633,11 +4629,10 @@ package body Sem_Ch12 is
-- The instantiation results in a guaranteed ABE -- The instantiation results in a guaranteed ABE
if Is_Known_Guaranteed_ABE (N) and then Needs_Body then if Is_Known_Guaranteed_ABE (N) and then Needs_Body then
-- Do not instantiate the corresponding body because gigi cannot -- Do not instantiate the corresponding body because gigi cannot
-- handle certain types of premature instantiations. -- handle certain types of premature instantiations.
Pending_Instantiations.Decrement_Last; Remove_Dead_Instance (N);
-- Create completing bodies for all subprogram declarations since -- Create completing bodies for all subprogram declarations since
-- their real bodies will not be instantiated. -- their real bodies will not be instantiated.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment