blob: 40b09e2816d3a81a7106ba95ac83b6884c3295bd [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- E X P _ U N S T --
-- --
-- B o d y --
-- --
-- Copyright (C) 2014-2015, 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- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
-- for more details. You should have received a copy of the GNU General --
-- Public License distributed with GNAT; see file COPYING3. If not, go to --
-- http://www.gnu.org/licenses for a complete copy of the license. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
with Atree; use Atree;
with Einfo; use Einfo;
with Elists; use Elists;
with Exp_Util; use Exp_Util;
with Lib; use Lib;
with Namet; use Namet;
with Nlists; use Nlists;
with Nmake; use Nmake;
with Opt; use Opt;
with Rtsfind; use Rtsfind;
with Sinput; use Sinput;
with Sem; use Sem;
with Sem_Ch8; use Sem_Ch8;
with Sem_Mech; use Sem_Mech;
with Sem_Res; use Sem_Res;
with Sem_Util; use Sem_Util;
with Sinfo; use Sinfo;
with Snames; use Snames;
with Table;
with Tbuild; use Tbuild;
with Uintp; use Uintp;
package body Exp_Unst is
-- Tables used by Unnest_Subprogram
type Subp_Entry is record
Ent : Entity_Id;
-- Entity of the subprogram
Bod : Node_Id;
-- Subprogram_Body node for this subprogram
Lev : Nat;
-- Subprogram level (1 = outer subprogram (Subp argument), 2 = nested
-- immediately within this outer subprogram etc.)
Urefs : Elist_Id;
-- This is a copy of the Uplevel_References field from the entity for
-- the subprogram. Copy this to reuse the field for Subps_Index.
ARECnF : Entity_Id;
-- This entity is defined for all subprograms with uplevel references
-- except for the top-level subprogram (Subp itself). It is the entity
-- for the formal which is added to the parameter list to pass the
-- pointer to the activation record. Note that for this entity, n is
-- one less than the current level.
ARECn : Entity_Id;
ARECnT : Entity_Id;
ARECnPT : Entity_Id;
ARECnP : Entity_Id;
-- These AREC entities are defined only for subprograms for which we
-- generate an activation record declaration, i.e. for subprograms
-- with at least one nested subprogram that have uplevel referennces.
-- They are set to Empty for all other cases.
ARECnU : Entity_Id;
-- This AREC entity is the uplink component. It is other than Empty only
-- for nested subprograms that themselves have nested subprograms and
-- have uplevel references. Note that the n here is one less than the
-- level of the subprogram defining the activation record.
end record;
subtype SI_Type is Nat;
package Subps is new Table.Table (
Table_Component_Type => Subp_Entry,
Table_Index_Type => SI_Type,
Table_Low_Bound => 1,
Table_Initial => 100,
Table_Increment => 200,
Table_Name => "Unnest_Subps");
-- Records the subprograms in the nest whose outer subprogram is Subp
type Call_Entry is record
N : Node_Id;
-- The actual call
From : Entity_Id;
-- Entity of the subprogram containing the call
To : Entity_Id;
-- Entity of the subprogram called
end record;
package Calls is new Table.Table (
Table_Component_Type => Call_Entry,
Table_Index_Type => Nat,
Table_Low_Bound => 1,
Table_Initial => 100,
Table_Increment => 200,
Table_Name => "Unnest_Calls");
-- Records each call within the outer subprogram and all nested subprograms
-- that are to other subprograms nested within the outer subprogram. These
-- are the calls that may need an additional parameter.
-------------------------------------
-- Check_Uplevel_Reference_To_Type --
-------------------------------------
procedure Check_Uplevel_Reference_To_Type (Typ : Entity_Id) is
function Check_Dynamic_Type (T : Entity_Id) return Boolean;
-- This is an internal recursive routine that checks if T or any of
-- its subsdidiary types are dynamic. If so, then the original Typ is
-- marked as having an uplevel reference, as is the subsidiary type in
-- question, and any referenced dynamic bounds are also marked as having
-- an uplevel reference, and True is returned. If the type is a static
-- type, then False is returned;
------------------------
-- Check_Dynamic_Type --
------------------------
function Check_Dynamic_Type (T : Entity_Id) return Boolean is
DT : Boolean := False;
begin
-- If it's a static type, nothing to do
if Is_Static_Type (T) then
return False;
-- If the type is uplevel referenced, then it must be dynamic
elsif Has_Uplevel_Reference (T) then
Set_Has_Uplevel_Reference (Typ);
return True;
-- If the type is at library level, always consider it static, since
-- uplevel references do not matter in this case.
elsif Is_Library_Level_Entity (T) then
Set_Is_Static_Type (T);
return False;
-- Otherwise we need to figure out what the story is with this type
else
DT := False;
-- For a scalar type, check bounds
if Is_Scalar_Type (T) then
-- If both bounds static, then this is a static type
declare
LB : constant Node_Id := Type_Low_Bound (T);
UB : constant Node_Id := Type_High_Bound (T);
begin
if not Is_Static_Expression (LB) then
Set_Has_Uplevel_Reference (Entity (LB));
DT := True;
end if;
if not Is_Static_Expression (UB) then
Set_Has_Uplevel_Reference (Entity (UB));
DT := True;
end if;
end;
-- For record type, check all components
elsif Is_Record_Type (T) then
declare
C : Entity_Id;
begin
C := First_Component_Or_Discriminant (T);
while Present (C) loop
if Check_Dynamic_Type (Etype (C)) then
DT := True;
end if;
Next_Component_Or_Discriminant (C);
end loop;
end;
-- For array type, check index types and component type
elsif Is_Array_Type (T) then
declare
IX : Node_Id;
begin
if Check_Dynamic_Type (Component_Type (T)) then
DT := True;
end if;
IX := First_Index (T);
while Present (IX) loop
if Check_Dynamic_Type (Etype (IX)) then
DT := True;
end if;
Next_Index (IX);
end loop;
end;
-- For now, ignore other types
else
return False;
end if;
-- See if we marked that type as dynamic
if DT then
Set_Has_Uplevel_Reference (T);
Set_Has_Uplevel_Reference (Typ);
return True;
-- If not mark it as static
else
Set_Is_Static_Type (T);
return False;
end if;
end if;
end Check_Dynamic_Type;
-- Start of processing for Check_Uplevel_Reference_To_Type
begin
-- Nothing to do inside a generic (all processing is for instance)
if Inside_A_Generic then
return;
-- Nothing to do if we know this is a static type
elsif Is_Static_Type (Typ) then
return;
-- Nothing to do if already marked as uplevel referenced
elsif Has_Uplevel_Reference (Typ) then
return;
-- Otherwise check if we have a dynamic type
else
if Check_Dynamic_Type (Typ) then
Set_Has_Uplevel_Reference (Typ);
end if;
end if;
null;
end Check_Uplevel_Reference_To_Type;
----------------------------
-- Note_Uplevel_Reference --
----------------------------
procedure Note_Uplevel_Reference (N : Node_Id; Subp : Entity_Id) is
Elmt : Elmt_Id;
begin
-- Nothing to do inside a generic (all processing is for instance)
if Inside_A_Generic then
return;
end if;
-- Nothing to do if reference has no entity field
if Nkind (N) not in N_Has_Entity then
return;
end if;
-- Establish list if first call for Uplevel_References
if No (Uplevel_References (Subp)) then
Set_Uplevel_References (Subp, New_Elmt_List);
end if;
-- Ignore if node is already in the list. This is a bit inefficient,
-- but we can definitely get duplicates that cause trouble!
Elmt := First_Elmt (Uplevel_References (Subp));
while Present (Elmt) loop
if N = Node (Elmt) then
return;
else
Next_Elmt (Elmt);
end if;
end loop;
-- Add new entry to Uplevel_References. Each entry is two elements of
-- the list. The first is the actual reference, the second is the
-- enclosing subprogram at the point of reference
Append_Elmt (N, Uplevel_References (Subp));
if Is_Subprogram (Current_Scope) then
Append_Elmt (Current_Scope, Uplevel_References (Subp));
else
Append_Elmt
(Enclosing_Subprogram (Current_Scope), Uplevel_References (Subp));
end if;
Set_Has_Uplevel_Reference (Entity (N));
Set_Has_Uplevel_Reference (Subp);
end Note_Uplevel_Reference;
-----------------------
-- Unnest_Subprogram --
-----------------------
procedure Unnest_Subprogram (Subp : Entity_Id; Subp_Body : Node_Id) is
function Actual_Ref (N : Node_Id) return Node_Id;
-- This function is applied to an element in the Uplevel_References
-- list, and it finds the actual reference. Often this is just N itself,
-- but in some cases it gets rewritten, e.g. as a Type_Conversion, and
-- this function digs out the actual reference
function AREC_String (Lev : Pos) return String;
-- Given a level value, 1, 2, ... returns the string AREC, AREC2, ...
function Enclosing_Subp (Subp : SI_Type) return SI_Type;
-- Subp is the index of a subprogram which has a Lev greater than 1.
-- This function returns the index of the enclosing subprogram which
-- will have a Lev value one less than this.
function Get_Level (Sub : Entity_Id) return Nat;
-- Sub is either Subp itself, or a subprogram nested within Subp. This
-- function returns the level of nesting (Subp = 1, subprograms that
-- are immediately nested within Subp = 2, etc).
function Subp_Index (Sub : Entity_Id) return SI_Type;
-- Given the entity for a subprogram, return corresponding Subps index
----------------
-- Actual_Ref --
----------------
function Actual_Ref (N : Node_Id) return Node_Id is
begin
case Nkind (N) is
-- If we have an entity reference, then this is the actual ref
when N_Has_Entity =>
return N;
-- For a type conversion, go get the expression
when N_Type_Conversion =>
return Expression (N);
-- For an explicit dereference, get the prefix
when N_Explicit_Dereference =>
return Prefix (N);
-- No other possibilities should exist
when others =>
raise Program_Error;
end case;
end Actual_Ref;
-----------------
-- AREC_String --
-----------------
function AREC_String (Lev : Pos) return String is
begin
if Lev > 9 then
return AREC_String (Lev / 10) & Character'Val (Lev mod 10 + 48);
else
return "AREC" & Character'Val (Lev + 48);
end if;
end AREC_String;
--------------------
-- Enclosing_Subp --
--------------------
function Enclosing_Subp (Subp : SI_Type) return SI_Type is
STJ : Subp_Entry renames Subps.Table (Subp);
Ret : constant SI_Type := Subp_Index (Enclosing_Subprogram (STJ.Ent));
begin
pragma Assert (STJ.Lev > 1);
pragma Assert (Subps.Table (Ret).Lev = STJ.Lev - 1);
return Ret;
end Enclosing_Subp;
---------------
-- Get_Level --
---------------
function Get_Level (Sub : Entity_Id) return Nat is
Lev : Nat;
S : Entity_Id;
begin
Lev := 1;
S := Sub;
loop
if S = Subp then
return Lev;
else
S := Enclosing_Subprogram (S);
Lev := Lev + 1;
end if;
end loop;
end Get_Level;
----------------
-- Subp_Index --
----------------
function Subp_Index (Sub : Entity_Id) return SI_Type is
begin
pragma Assert (Is_Subprogram (Sub));
return SI_Type (UI_To_Int (Subps_Index (Sub)));
end Subp_Index;
-- Start of processing for Unnest_Subprogram
begin
-- Nothing to do inside a generic (all processing is for instance)
if Inside_A_Generic then
return;
end if;
-- At least for now, do not unnest anything but main source unit
if not In_Extended_Main_Source_Unit (Subp_Body) then
return;
end if;
-- First step, we must mark all nested subprograms that require a static
-- link (activation record) because either they contain explicit uplevel
-- references (as indicated by Has_Uplevel_Reference being set at this
-- point), or they make calls to other subprograms in the same nest that
-- require a static link (in which case we set this flag).
-- This is a recursive definition, and to implement this, we have to
-- build a call graph for the set of nested subprograms, and then go
-- over this graph to implement recursively the invariant that if a
-- subprogram has a call to a subprogram requiring a static link, then
-- the calling subprogram requires a static link.
-- First populate the above tables
Subps.Init;
Calls.Init;
Build_Tables : declare
function Visit_Node (N : Node_Id) return Traverse_Result;
-- Visit a single node in Subp
----------------
-- Visit_Node --
----------------
function Visit_Node (N : Node_Id) return Traverse_Result is
Ent : Entity_Id;
Csub : Entity_Id;
function Find_Current_Subprogram return Entity_Id;
-- Finds the current subprogram containing the call N
-----------------------------
-- Find_Current_Subprogram --
-----------------------------
function Find_Current_Subprogram return Entity_Id is
Nod : Node_Id;
begin
Nod := N;
loop
Nod := Parent (Nod);
if Nkind (Nod) = N_Subprogram_Body then
if Acts_As_Spec (Nod) then
return Defining_Entity (Specification (Nod));
else
return Corresponding_Spec (Nod);
end if;
end if;
end loop;
end Find_Current_Subprogram;
-- Start of processing for Visit_Node
begin
-- Record a call
if Nkind_In (N, N_Procedure_Call_Statement, N_Function_Call)
-- We are only interested in direct calls, not indirect calls
-- (where Name (N) is an explicit dereference) at least for now!
and then Nkind (Name (N)) in N_Has_Entity
then
Ent := Entity (Name (N));
-- We are only interested in calls to subprograms nested
-- within Subp. Calls to Subp itself or to subprograms that
-- are outside the nested structure do not affect us.
if Scope_Within (Ent, Subp) then
-- For now, ignore calls to generic instances. Seems to be
-- some problem there which we will investigate later ???
if Original_Location (Sloc (Ent)) /= Sloc (Ent)
or else Is_Generic_Instance (Ent)
then
null;
-- Ignore calls to imported routines
elsif Is_Imported (Ent) then
null;
-- Here we have a call to keep and analyze
else
Csub := Find_Current_Subprogram;
-- Both caller and callee must be subprograms (we ignore
-- generic subprograms).
if Is_Subprogram (Csub) and then Is_Subprogram (Ent) then
Calls.Append ((N, Find_Current_Subprogram, Ent));
end if;
end if;
end if;
-- Record a subprogram. We record a subprogram body that acts as
-- a spec. Otherwise we record a subprogram declaration, providing
-- that it has a corresponding body we can get hold of. The case
-- of no corresponding body being available is ignored for now.
elsif (Nkind (N) = N_Subprogram_Body and then Acts_As_Spec (N))
or else (Nkind (N) = N_Subprogram_Declaration
and then Present (Corresponding_Body (N)))
then
Subps.Increment_Last;
declare
STJ : Subp_Entry renames Subps.Table (Subps.Last);
begin
-- Set fields of Subp_Entry for new subprogram
STJ.Ent := Defining_Entity (Specification (N));
STJ.Lev := Get_Level (STJ.Ent);
if Nkind (N) = N_Subprogram_Body then
STJ.Bod := N;
else
STJ.Bod :=
Parent (Declaration_Node (Corresponding_Body (N)));
pragma Assert (Nkind (STJ.Bod) = N_Subprogram_Body);
end if;
-- Capture Uplevel_References, and then set (uses the same
-- field), the Subps_Index value for this subprogram.
STJ.Urefs := Uplevel_References (STJ.Ent);
Set_Subps_Index (STJ.Ent, UI_From_Int (Int (Subps.Last)));
end;
end if;
return OK;
end Visit_Node;
-----------
-- Visit --
-----------
procedure Visit is new Traverse_Proc (Visit_Node);
-- Used to traverse the body of Subp, populating the tables
-- Start of processing for Build_Tables
begin
-- A special case, if the outer level subprogram has a separate spec
-- then we won't catch it in the traversal of the body. But we do
-- want to visit the declaration in this case!
if not Acts_As_Spec (Subp_Body) then
declare
Dummy : Traverse_Result;
Decl : constant Node_Id :=
Parent (Declaration_Node (Corresponding_Spec (Subp_Body)));
pragma Assert (Nkind (Decl) = N_Subprogram_Declaration);
begin
Dummy := Visit_Node (Decl);
end;
end if;
-- Traverse the body to get the rest of the subprograms and calls
Visit (Subp_Body);
end Build_Tables;
-- Second step is to do the transitive closure, if any subprogram has
-- a call to a subprogram for which Has_Uplevel_Reference is set, then
-- we set Has_Uplevel_Reference for the calling routine.
Closure : declare
Modified : Boolean;
begin
-- We use a simple minded algorithm as follows (obviously this can
-- be done more efficiently, using one of the standard algorithms
-- for efficient transitive closure computation, but this is simple
-- and most likely fast enough that its speed does not matter).
-- Repeatedly scan the list of calls. Any time we find a call from
-- A to B, where A does not have Has_Uplevel_Reference, and B does
-- have this flag set, then set the flag for A, and note that we
-- have made a change by setting Modified True. We repeat this until
-- we make a pass with no modifications.
Outer : loop
Modified := False;
Inner : for J in Calls.First .. Calls.Last loop
if not Has_Uplevel_Reference (Calls.Table (J).From)
and then Has_Uplevel_Reference (Calls.Table (J).To)
then
Set_Has_Uplevel_Reference (Calls.Table (J).From);
Modified := True;
end if;
end loop Inner;
exit Outer when not Modified;
end loop Outer;
end Closure;
-- Next step, create the entities for code we will insert. We do this
-- at the start so that all the entities are defined, regardless of the
-- order in which we do the code insertions.
Create_Entities : for J in Subps.First .. Subps.Last loop
declare
STJ : Subp_Entry renames Subps.Table (J);
Loc : constant Source_Ptr := Sloc (STJ.Bod);
ARS : constant String := AREC_String (STJ.Lev);
begin
-- First we create the ARECnF entity for the additional formal
-- for all subprograms requiring that an activation record pointer
-- be passed. This is true of all subprograms that have uplevel
-- references, and whose enclosing subprogram also has uplevel
-- references.
if Has_Uplevel_Reference (STJ.Ent)
and then STJ.Ent /= Subp
and then Has_Uplevel_Reference (Enclosing_Subprogram (STJ.Ent))
then
STJ.ARECnF :=
Make_Defining_Identifier (Loc,
Chars => Name_Find_Str (AREC_String (STJ.Lev - 1) & "F"));
else
STJ.ARECnF := Empty;
end if;
-- Now define the AREC entities for the activation record. This
-- is needed for any subprogram that has nested subprograms and
-- has uplevel references.
if Has_Nested_Subprogram (STJ.Ent)
and then Has_Uplevel_Reference (STJ.Ent)
then
STJ.ARECn :=
Make_Defining_Identifier (Loc, Name_Find_Str (ARS));
STJ.ARECnT :=
Make_Defining_Identifier (Loc, Name_Find_Str (ARS & "T"));
STJ.ARECnPT :=
Make_Defining_Identifier (Loc, Name_Find_Str (ARS & "PT"));
STJ.ARECnP :=
Make_Defining_Identifier (Loc, Name_Find_Str (ARS & "P"));
else
STJ.ARECn := Empty;
STJ.ARECnT := Empty;
STJ.ARECnPT := Empty;
STJ.ARECnP := Empty;
STJ.ARECnU := Empty;
end if;
-- Define uplink component entity if inner nesting case
if Has_Uplevel_Reference (STJ.Ent) and then STJ.Lev > 1 then
declare
ARS1 : constant String := AREC_String (STJ.Lev - 1);
begin
STJ.ARECnU :=
Make_Defining_Identifier (Loc,
Chars => Name_Find_Str (ARS1 & "U"));
end;
else
STJ.ARECnU := Empty;
end if;
end;
end loop Create_Entities;
-- Loop through subprograms
Subp_Loop : declare
Addr : constant Entity_Id := RTE (RE_Address);
begin
for J in Subps.First .. Subps.Last loop
declare
STJ : Subp_Entry renames Subps.Table (J);
begin
-- First add the extra formal if needed. This applies to all
-- nested subprograms that require an activation record to be
-- passed, as indicated by ARECnF being defined.
if Present (STJ.ARECnF) then
-- Here we need the extra formal. We do the expansion and
-- analysis of this manually, since it is fairly simple,
-- and it is not obvious how we can get what we want if we
-- try to use the normal Analyze circuit.
Add_Extra_Formal : declare
Encl : constant SI_Type := Enclosing_Subp (J);
STJE : Subp_Entry renames Subps.Table (Encl);
-- Index and Subp_Entry for enclosing routine
Form : constant Entity_Id := STJ.ARECnF;
-- The formal to be added. Note that n here is one less
-- than the level of the subprogram itself (STJ.Ent).
procedure Add_Form_To_Spec (F : Entity_Id; S : Node_Id);
-- S is an N_Function/Procedure_Specification node, and F
-- is the new entity to add to this subprogramn spec as
-- the last Extra_Formal.
----------------------
-- Add_Form_To_Spec --
----------------------
procedure Add_Form_To_Spec (F : Entity_Id; S : Node_Id) is
Sub : constant Entity_Id := Defining_Entity (S);
Ent : Entity_Id;
begin
-- Case of at least one Extra_Formal is present, set
-- ARECnF as the new last entry in the list.
if Present (Extra_Formals (Sub)) then
Ent := Extra_Formals (Sub);
while Present (Extra_Formal (Ent)) loop
Ent := Extra_Formal (Ent);
end loop;
Set_Extra_Formal (Ent, F);
-- No Extra formals present
else
Set_Extra_Formals (Sub, F);
Ent := Last_Formal (Sub);
if Present (Ent) then
Set_Extra_Formal (Ent, F);
end if;
end if;
end Add_Form_To_Spec;
-- Start of processing for Add_Extra_Formal
begin
-- Decorate the new formal entity
Set_Scope (Form, STJ.Ent);
Set_Ekind (Form, E_In_Parameter);
Set_Etype (Form, STJE.ARECnPT);
Set_Mechanism (Form, By_Copy);
Set_Never_Set_In_Source (Form, True);
Set_Analyzed (Form, True);
Set_Comes_From_Source (Form, False);
-- Case of only body present
if Acts_As_Spec (STJ.Bod) then
Add_Form_To_Spec (Form, Specification (STJ.Bod));
-- Case of separate spec
else
Add_Form_To_Spec (Form, Parent (STJ.Ent));
end if;
end Add_Extra_Formal;
end if;
-- Processing for subprograms that have at least one nested
-- subprogram, and have uplevel references.
if Has_Nested_Subprogram (STJ.Ent)
and then Has_Uplevel_Reference (STJ.Ent)
then
-- Local declarations for one such subprogram
declare
Loc : constant Source_Ptr := Sloc (STJ.Bod);
Elmt : Elmt_Id;
Nod : Node_Id;
Ent : Entity_Id;
Clist : List_Id;
Comp : Entity_Id;
Decl_ARECnT : Node_Id;
Decl_ARECn : Node_Id;
Decl_ARECnPT : Node_Id;
Decl_ARECnP : Node_Id;
-- Declaration nodes for the AREC entities we build
Uplevel_Entities :
array (1 .. List_Length (STJ.Urefs)) of Entity_Id;
Num_Uplevel_Entities : Nat;
-- Uplevel_Entities (1 .. Num_Uplevel_Entities) contains
-- a list (with no duplicates) of the entities for this
-- subprogram that are referenced uplevel. The maximum
-- number of entries cannot exceed the total number of
-- uplevel references.
begin
-- Populate the Uplevel_Entities array, using the flag
-- Uplevel_Reference_Noted to avoid duplicates.
Num_Uplevel_Entities := 0;
if Present (STJ.Urefs) then
Elmt := First_Elmt (STJ.Urefs);
while Present (Elmt) loop
Nod := Actual_Ref (Node (Elmt));
Ent := Entity (Nod);
if not Uplevel_Reference_Noted (Ent) then
Set_Uplevel_Reference_Noted (Ent, True);
Num_Uplevel_Entities := Num_Uplevel_Entities + 1;
Uplevel_Entities (Num_Uplevel_Entities) := Ent;
end if;
Next_Elmt (Elmt);
Next_Elmt (Elmt);
end loop;
end if;
-- Build list of component declarations for ARECnT
Clist := Empty_List;
-- If we are in a subprogram that has a static link that
-- ias passed in (as indicated by ARECnF being deinfed),
-- then include ARECnU : ARECnPT := ARECnF where n is
-- one less than the current level and the entity ARECnPT
-- comes from the enclosing subprogram.
if Present (STJ.ARECnF) then
declare
STJE : Subp_Entry
renames Subps.Table (Enclosing_Subp (J));
begin
Append_To (Clist,
Make_Component_Declaration (Loc,
Defining_Identifier => STJ.ARECnU,
Component_Definition =>
Make_Component_Definition (Loc,
Subtype_Indication =>
New_Occurrence_Of (STJE.ARECnPT, Loc)),
Expression =>
New_Occurrence_Of (STJ.ARECnF, Loc)));
end;
end if;
-- Add components for uplevel referenced entities
for J in 1 .. Num_Uplevel_Entities loop
Comp :=
Make_Defining_Identifier (Loc,
Chars => Chars (Uplevel_Entities (J)));
Set_Activation_Record_Component
(Uplevel_Entities (J), Comp);
Append_To (Clist,
Make_Component_Declaration (Loc,
Defining_Identifier => Comp,
Component_Definition =>
Make_Component_Definition (Loc,
Subtype_Indication =>
New_Occurrence_Of (Addr, Loc))));
end loop;
-- Now we can insert the AREC declarations into the body
-- type ARECnT is record .. end record;
Decl_ARECnT :=
Make_Full_Type_Declaration (Loc,
Defining_Identifier => STJ.ARECnT,
Type_Definition =>
Make_Record_Definition (Loc,
Component_List =>
Make_Component_List (Loc,
Component_Items => Clist)));
-- ARECn : aliased ARECnT;
Decl_ARECn :=
Make_Object_Declaration (Loc,
Defining_Identifier => STJ.ARECn,
Aliased_Present => True,
Object_Definition =>
New_Occurrence_Of (STJ.ARECnT, Loc));
-- type ARECnPT is access all ARECnT;
Decl_ARECnPT :=
Make_Full_Type_Declaration (Loc,
Defining_Identifier => STJ.ARECnPT,
Type_Definition =>
Make_Access_To_Object_Definition (Loc,
All_Present => True,
Subtype_Indication =>
New_Occurrence_Of (STJ.ARECnT, Loc)));
-- ARECnP : constant ARECnPT := ARECn'Access;
Decl_ARECnP :=
Make_Object_Declaration (Loc,
Defining_Identifier => STJ.ARECnP,
Constant_Present => True,
Object_Definition =>
New_Occurrence_Of (STJ.ARECnPT, Loc),
Expression =>
Make_Attribute_Reference (Loc,
Prefix =>
New_Occurrence_Of (STJ.ARECn, Loc),
Attribute_Name => Name_Access));
Prepend_List_To (Declarations (STJ.Bod),
New_List
(Decl_ARECnT, Decl_ARECn, Decl_ARECnPT, Decl_ARECnP));
-- Analyze the newly inserted declarations. Note that we
-- do not need to establish the whole scope stack, since
-- we have already set all entity fields (so there will
-- be no searching of upper scopes to resolve names). But
-- we do set the scope of the current subprogram, so that
-- newly created entities go in the right entity chain.
-- We analyze with all checks suppressed (since we do
-- not expect any exceptions, and also we temporarily
-- turn off Unested_Subprogram_Mode to avoid trying to
-- mark uplevel references (not needed at this stage,
-- and in fact causes a bit of recursive chaos).
Push_Scope (STJ.Ent);
Opt.Unnest_Subprogram_Mode := False;
Analyze (Decl_ARECnT, Suppress => All_Checks);
Analyze (Decl_ARECn, Suppress => All_Checks);
Analyze (Decl_ARECnPT, Suppress => All_Checks);
Analyze (Decl_ARECnP, Suppress => All_Checks);
Opt.Unnest_Subprogram_Mode := True;
Pop_Scope;
-- Next step, for each uplevel referenced entity, add
-- assignment operations to set the comoponent in the
-- activation record.
for J in 1 .. Num_Uplevel_Entities loop
declare
Ent : constant Entity_Id := Uplevel_Entities (J);
Loc : constant Source_Ptr := Sloc (Ent);
Dec : constant Node_Id := Declaration_Node (Ent);
Ins : Node_Id;
Asn : Node_Id;
begin
-- For parameters, we insert the assignment right
-- after the declaration of ARECnP. For all other
-- entities, we insert the assignment immediately
-- after the declaration of the entity.
-- Note: we don't need to mark the entity as being
-- aliased, because the address attribute will mark
-- it as Address_Taken, and that is good enough.
if Is_Formal (Ent) then
Ins := Decl_ARECnP;
else
Ins := Dec;
end if;
-- Build and insert the assignment:
-- ARECn.nam := nam
Asn :=
Make_Assignment_Statement (Loc,
Name =>
Make_Selected_Component (Loc,
Prefix =>
New_Occurrence_Of (STJ.ARECn, Loc),
Selector_Name =>
Make_Identifier (Loc, Chars (Ent))),
Expression =>
Make_Attribute_Reference (Loc,
Prefix =>
New_Occurrence_Of (Ent, Loc),
Attribute_Name => Name_Address));
Insert_After (Ins, Asn);
-- Analyze the assignment statement. We do not need
-- to establish the relevant scope stack entries
-- here, because we have already set the correct
-- entity references, so no name resolution is
-- required, and no new entities are created, so
-- we don't even need to set the current scope.
-- We analyze with all checks suppressed (since
-- we do not expect any exceptions, and also we
-- temporarily turn off Unested_Subprogram_Mode
-- to avoid trying to mark uplevel references (not
-- needed at this stage, and in fact causes a bit
-- of recursive chaos).
Opt.Unnest_Subprogram_Mode := False;
Analyze (Asn, Suppress => All_Checks);
Opt.Unnest_Subprogram_Mode := True;
end;
end loop;
end;
end if;
end;
end loop;
end Subp_Loop;
-- Next step, process uplevel references. This has to be done in a
-- separate pass, after completing the processing in Sub_Loop because we
-- need all the AREC declarations generated, inserted, and analyzed so
-- that the uplevel references can be successfully analyzed.
Uplev_Refs : for J in Subps.First .. Subps.Last loop
declare
STJ : Subp_Entry renames Subps.Table (J);
begin
-- We are only interested in entries which have uplevel references
-- to deal with, as indicated by the Urefs list being present
if Present (STJ.Urefs) then
-- Process uplevel references for one subprogram
declare
Elmt : Elmt_Id;
begin
-- Loop through uplevel references
Elmt := First_Elmt (STJ.Urefs);
while Present (Elmt) loop
-- Rewrite one reference
declare
Ref : constant Node_Id := Actual_Ref (Node (Elmt));
-- The reference to be rewritten
Loc : constant Source_Ptr := Sloc (Ref);
-- Source location for the reference
Ent : constant Entity_Id := Entity (Ref);
-- The referenced entity
Typ : constant Entity_Id := Etype (Ent);
-- The type of the referenced entity
Rsub : constant Entity_Id :=
Node (Next_Elmt (Elmt));
-- The enclosing subprogram for the reference
RSX : constant SI_Type := Subp_Index (Rsub);
-- Subp_Index for enclosing subprogram for ref
STJR : Subp_Entry renames Subps.Table (RSX);
-- Subp_Entry for enclosing subprogram for ref
Tnn : constant Entity_Id :=
Make_Temporary
(Loc, 'T', Related_Node => Ref);
-- Local pointer type for reference
Pfx : Node_Id;
Comp : Entity_Id;
SI : SI_Type;
begin
-- Push the current scope, so that the pointer type
-- Tnn, and any subsidiary entities resulting from
-- the analysis of the rewritten reference, go in the
-- right entity chain.
Push_Scope (STJR.Ent);
-- First insert declaration for pointer type
-- type Tnn is access all typ;
Insert_Action (Node (Elmt),
Make_Full_Type_Declaration (Loc,
Defining_Identifier => Tnn,
Type_Definition =>
Make_Access_To_Object_Definition (Loc,
All_Present => True,
Subtype_Indication =>
New_Occurrence_Of (Typ, Loc))));
-- Now we need to rewrite the reference. We have a
-- reference is from level STJE.Lev to level STJ.Lev.
-- The general form of the rewritten reference for
-- entity X is:
-- Tnn!(ARECaF.ARECbU.ARECcU.ARECdU....ARECm.X).all
-- where a,b,c,d .. m =
-- STJR.Lev - 1, STJ.Lev - 2, .. STJ.Lev
pragma Assert (STJR.Lev > STJ.Lev);
-- Compute the prefix of X. Here are examples to make
-- things clear (with parens to show groupings, the
-- prefix is everything except the .X at the end).
-- level 2 to level 1
-- AREC1F.X
-- level 3 to level 1
-- (AREC2F.AREC1U).X
-- level 4 to level 1
-- ((AREC3F.AREC2U).AREC1U).X
-- level 6 to level 2
-- (((AREC5F.AREC4U).AREC3U).AREC2U).X
Pfx := New_Occurrence_Of (STJR.ARECnF, Loc);
SI := RSX;
for L in STJ.Lev .. STJR.Lev - 2 loop
SI := Enclosing_Subp (SI);
Pfx :=
Make_Selected_Component (Loc,
Prefix => Pfx,
Selector_Name =>
New_Occurrence_Of
(Subps.Table (SI).ARECnU, Loc));
end loop;
-- Get activation record component (must exist)
Comp := Activation_Record_Component (Ent);
pragma Assert (Present (Comp));
-- Do the replacement
Rewrite (Ref,
Make_Explicit_Dereference (Loc,
Prefix =>
Unchecked_Convert_To (Tnn,
Make_Selected_Component (Loc,
Prefix => Pfx,
Selector_Name =>
New_Occurrence_Of (Comp, Loc)))));
-- Analyze and resolve the new expression. We do not
-- need to establish the relevant scope stack entries
-- here, because we have already set all the correct
-- entity references, so no name resolution is needed.
-- We have already set the current scope, so that any
-- new entities created will be in the right scope.
-- We analyze with all checks suppressed (since we do
-- not expect any exceptions, and also we temporarily
-- turn off Unested_Subprogram_Mode to avoid trying to
-- mark uplevel references (not needed at this stage,
-- and in fact causes a bit of recursive chaos).
Opt.Unnest_Subprogram_Mode := False;
Analyze_And_Resolve (Ref, Typ, Suppress => All_Checks);
Opt.Unnest_Subprogram_Mode := True;
Pop_Scope;
end;
Next_Elmt (Elmt);
Next_Elmt (Elmt);
end loop;
end;
end if;
end;
end loop Uplev_Refs;
-- Finally, loop through all calls adding extra actual for the
-- activation record where it is required.
Adjust_Calls : for J in Calls.First .. Calls.Last loop
-- Process a single call, we are only interested in a call to a
-- subprogram that actually needs a pointer to an activation record,
-- as indicated by the ARECnF entity being set. This excludes the
-- top level subprogram, and any subprogram not having uplevel refs.
Adjust_One_Call : declare
CTJ : Call_Entry renames Calls.Table (J);
STF : Subp_Entry renames Subps.Table (Subp_Index (CTJ.From));
STT : Subp_Entry renames Subps.Table (Subp_Index (CTJ.To));
Loc : constant Source_Ptr := Sloc (CTJ.N);
Extra : Node_Id;
ExtraP : Node_Id;
SubX : SI_Type;
Act : Node_Id;
begin
if Present (STT.ARECnF) then
-- CTJ.N is a call to a subprogram which may require
-- a pointer to an activation record. The subprogram
-- containing the call is CTJ.From and the subprogram being
-- called is CTJ.To, so we have a call from level STF.Lev to
-- level STT.Lev.
-- There are three possibilities:
-- For a call to the same level, we just pass the activation
-- record passed to the calling subprogram.
if STF.Lev = STT.Lev then
Extra := New_Occurrence_Of (STF.ARECnF, Loc);
-- For a call that goes down a level, we pass a pointer
-- to the activation record constructed wtihin the caller
-- (which may be the outer level subprogram, but also may
-- be a more deeply nested caller).
elsif STT.Lev = STF.Lev + 1 then
Extra := New_Occurrence_Of (STF.ARECnP, Loc);
-- Otherwise we must have an upcall (STT.Lev < STF.LEV),
-- since it is not possible to do a downcall of more than
-- one level.
-- For a call from level STF.Lev to level STT.Lev, we
-- have to find the activation record needed by the
-- callee. This is as follows:
-- ARECaF.ARECbU.ARECcU....ARECm
-- where a,b,c .. m =
-- STF.Lev - 1, STF.Lev - 2, STF.Lev - 3 .. STT.Lev
else
pragma Assert (STT.Lev < STF.Lev);
Extra := New_Occurrence_Of (STF.ARECnF, Loc);
SubX := Subp_Index (CTJ.From);
for K in reverse STT.Lev .. STF.Lev - 1 loop
SubX := Enclosing_Subp (SubX);
Extra :=
Make_Selected_Component (Loc,
Prefix => Extra,
Selector_Name =>
New_Occurrence_Of
(Subps.Table (SubX).ARECnU, Loc));
end loop;
end if;
-- Extra is the additional parameter to be added. Build a
-- parameter association that we can append to the actuals.
ExtraP :=
Make_Parameter_Association (Loc,
Selector_Name =>
New_Occurrence_Of (STT.ARECnF, Loc),
Explicit_Actual_Parameter => Extra);
if No (Parameter_Associations (CTJ.N)) then
Set_Parameter_Associations (CTJ.N, Empty_List);
end if;
Append (ExtraP, Parameter_Associations (CTJ.N));
-- We need to deal with the actual parameter chain as well.
-- The newly added parameter is always the last actual.
Act := First_Named_Actual (CTJ.N);
if No (Act) then
Set_First_Named_Actual (CTJ.N, Extra);
-- Here we must follow the chain and append the new entry
else
loop
declare
PAN : Node_Id;
NNA : Node_Id;
begin
PAN := Parent (Act);
pragma Assert (Nkind (PAN) = N_Parameter_Association);
NNA := Next_Named_Actual (PAN);
if No (NNA) then
Set_Next_Named_Actual (PAN, Extra);
exit;
end if;
Act := NNA;
end;
end loop;
end if;
-- Analyze and resolve the new actual. We do not need to
-- establish the relevant scope stack entries here, because
-- we have already set all the correct entity references, so
-- no name resolution is needed.
-- We analyze with all checks suppressed (since we do not
-- expect any exceptions, and also we temporarily turn off
-- Unested_Subprogram_Mode to avoid trying to mark uplevel
-- references (not needed at this stage, and in fact causes
-- a bit of recursive chaos).
Opt.Unnest_Subprogram_Mode := False;
Analyze_And_Resolve
(Extra, Etype (STT.ARECnF), Suppress => All_Checks);
Opt.Unnest_Subprogram_Mode := True;
end if;
end Adjust_One_Call;
end loop Adjust_Calls;
return;
end Unnest_Subprogram;
end Exp_Unst;