[Ada] GNAT-LLVM unnesting issues in elaboration code

gcc/ada/

	* exp_ch7.adb (Check_Unnesting_In_Decls_Or_Stmts): In the case
	of an if-statement, call Unnest_If_Statement to determine
	whether there are nested subprograms in any of the statement
	lists of the "if" parts that require a wrapping procedure to
	handle possible up-level refeferences.
	(Unnest_Block): Call Check_Unnesting_In_Handlers to do unnesting
	of subprograms in exception handlers of the block statement.
	(Unnest_If_Statement): New procedure to traverse the parts of an
	if-statement and create wrapper procedures as needed to
	encapsulate nested subprograms that may make up-level
	references.
	(Check_Stmts_For_Subp_Unnesting): New support procedure in
	Unnest_If_Statement to traverse a statement list looking for
	top-level subprogram bodies that require wrapping inside a
	procedure (via Unnest_Statement_List) as well as possibly having
	other statements (block, loop, if) that may themselves require
	an unnesting transformation (via
	Check_Unnesting_In_Decls_Or_Stmts).
	(Unnest_Statement_List): New support procedure to traverse the
	statements of a statement list that contains subprogram bodies
	at the top level and replace the statement list with a wrapper
	procedure body encapsulating the statements and a call to the
	procedure.
This commit is contained in:
Gary Dismukes 2020-06-24 17:22:58 -04:00 committed by Pierre-Marie de Rodat
parent 6f238680dd
commit 0574695888

View File

@ -398,6 +398,31 @@ package body Exp_Ch7 is
-- actions or secondary-stack management, in which case the nested
-- subprogram is a finalizer.
procedure Unnest_If_Statement (If_Stmt : Node_Id);
-- The separate statement lists associated with an if-statement (then part,
-- elsif parts, else part) may require unnesting if they directly contain
-- a subprogram body that references up-level objects. Each statement list
-- is traversed to locate such subprogram bodies, and if a part's statement
-- list contains a body, then the list is replaced with a new procedure
-- containing the part's statements followed by a call to the procedure.
-- Furthermore, any nested blocks, loops, or if statements will also be
-- traversed to determine the need for further unnesting transformations.
procedure Unnest_Statement_List (Stmts : in out List_Id);
-- A list of statements that directly contains a subprogram at its outer
-- level, that may reference objects declared in that same statement list,
-- is rewritten as a procedure containing the statement list Stmts (which
-- includes any such objects as well as the nested subprogram), followed by
-- a call to the new procedure, and Stmts becomes the list containing the
-- procedure and the call. This ensures that Unnest_Subprogram will later
-- properly handle up-level references from the nested subprogram to
-- objects declared earlier in statement list, by creating an activation
-- record and passing it to the nested subprogram. This procedure also
-- resets the Scope of objects declared in the statement list, as well as
-- the Scope of the nested subprogram, to refer to the new procedure.
-- Also, the new procedure is marked Has_Nested_Subprogram, so this should
-- only be called when known that the statement list contains a subprogram.
procedure Unnest_Loop (Loop_Stmt : Node_Id);
-- Top-level Loops that contain nested subprograms with up-level references
-- need to have activation records. We do this by rewriting the loop as a
@ -4233,6 +4258,17 @@ package body Exp_Ch7 is
then
Unnest_Block (Decl_Or_Stmt);
-- If-statements may contain subprogram bodies at the outer level
-- of their statement lists, and the subprograms may make up-level
-- references (such as to objects declared in the same statement
-- list). Unlike block and loop cases, however, we don't have an
-- entity on which to test the Contains_Subprogram flag, so
-- Unnest_If_Statement must traverse the statement lists to
-- determine whether there are nested subprograms present.
elsif Nkind (Decl_Or_Stmt) = N_If_Statement then
Unnest_If_Statement (Decl_Or_Stmt);
elsif Nkind (Decl_Or_Stmt) = N_Loop_Statement then
declare
Id : constant Entity_Id :=
@ -9261,6 +9297,11 @@ package body Exp_Ch7 is
Handled_Statement_Sequence =>
Handled_Statement_Sequence (Decl));
-- Handlers in the block may contain nested subprograms that require
-- unnesting.
Check_Unnesting_In_Handlers (Local_Body);
Rewrite (Decl, Local_Body);
Analyze (Decl);
Set_Has_Nested_Subprogram (Local_Proc);
@ -9288,6 +9329,94 @@ package body Exp_Ch7 is
end loop;
end Unnest_Block;
-------------------------
-- Unnest_If_Statement --
-------------------------
procedure Unnest_If_Statement (If_Stmt : Node_Id) is
procedure Check_Stmts_For_Subp_Unnesting (Stmts : in out List_Id);
-- A list of statements (that may be a list associated with a then,
-- elsif, or else part of an if-statement) is traversed at the top
-- level to determine whether it contains a subprogram body, and if so,
-- the statements will be replaced with a new procedure body containing
-- the statements followed by a call to the procedure. The individual
-- statements may also be blocks, loops, or other if statements that
-- themselves may require contain nested subprograms needing unnesting.
procedure Check_Stmts_For_Subp_Unnesting (Stmts : in out List_Id) is
Subp_Found : Boolean := False;
begin
if Is_Empty_List (Stmts) then
return;
end if;
declare
Stmt : Node_Id := First (Stmts);
begin
while Present (Stmt) loop
if Nkind (Stmt) = N_Subprogram_Body then
Subp_Found := True;
exit;
end if;
Next (Stmt);
end loop;
end;
-- The statements themselves may be blocks, loops, etc. that in turn
-- contain nested subprograms requiring an unnesting transformation.
-- We perform this traversal after looking for subprogram bodies, to
-- avoid considering procedures created for one of those statements
-- (such as a block rewritten as a procedure) as a nested subprogram
-- of the statement list (which could result in an unneeded wrapper
-- procedure).
Check_Unnesting_In_Decls_Or_Stmts (Stmts);
-- If there was a top-level subprogram body in the statement list,
-- then perform an unnesting transformation on the list by replacing
-- the statements with a wrapper procedure body containing the
-- original statements followed by a call to that procedure.
if Subp_Found then
Unnest_Statement_List (Stmts);
end if;
end Check_Stmts_For_Subp_Unnesting;
-- Local variables
Then_Stmts : List_Id := Then_Statements (If_Stmt);
Else_Stmts : List_Id := Else_Statements (If_Stmt);
-- Start of processing for Unnest_If_Statement
begin
Check_Stmts_For_Subp_Unnesting (Then_Stmts);
Set_Then_Statements (If_Stmt, Then_Stmts);
if not Is_Empty_List (Elsif_Parts (If_Stmt)) then
declare
Elsif_Part : Node_Id :=
First (Elsif_Parts (If_Stmt));
Elsif_Stmts : List_Id;
begin
while Present (Elsif_Part) loop
Elsif_Stmts := Then_Statements (Elsif_Part);
Check_Stmts_For_Subp_Unnesting (Elsif_Stmts);
Set_Then_Statements (Elsif_Part, Elsif_Stmts);
Next (Elsif_Part);
end loop;
end;
end if;
Check_Stmts_For_Subp_Unnesting (Else_Stmts);
Set_Else_Statements (If_Stmt, Else_Stmts);
end Unnest_If_Statement;
-----------------
-- Unnest_Loop --
-----------------
@ -9349,6 +9478,75 @@ package body Exp_Ch7 is
-- same loop entity that now belongs to the copied loop statement.
end Unnest_Loop;
---------------------------
-- Unnest_Statement_List --
---------------------------
procedure Unnest_Statement_List (Stmts : in out List_Id) is
Loc : constant Source_Ptr := Sloc (First (Stmts));
Local_Body : Node_Id;
Local_Call : Node_Id;
Local_Proc : Entity_Id;
New_Stmts : constant List_Id := Empty_List;
begin
Local_Proc :=
Make_Defining_Identifier (Loc,
Chars => New_Internal_Name ('P'));
Local_Body :=
Make_Subprogram_Body (Loc,
Specification =>
Make_Procedure_Specification (Loc,
Defining_Unit_Name => Local_Proc),
Declarations => Empty_List,
Handled_Statement_Sequence =>
Make_Handled_Sequence_Of_Statements (Loc,
Statements => Stmts));
Append_To (New_Stmts, Local_Body);
Analyze (Local_Body);
Set_Has_Nested_Subprogram (Local_Proc);
Local_Call :=
Make_Procedure_Call_Statement (Loc,
Name => New_Occurrence_Of (Local_Proc, Loc));
Append_To (New_Stmts, Local_Call);
Analyze (Local_Call);
-- Traverse the statements, and for any that are declarations or
-- subprogram bodies that have entities, set the Scope of those
-- entities to the new procedure's Entity_Id.
declare
Stmt : Node_Id := First (Stmts);
begin
while Present (Stmt) loop
case Nkind (Stmt) is
when N_Declaration
| N_Renaming_Declaration
=>
Set_Scope (Defining_Identifier (Stmt), Local_Proc);
when N_Subprogram_Body =>
Set_Scope
(Defining_Unit_Name (Specification (Stmt)), Local_Proc);
when others =>
null;
end case;
Next (Stmt);
end loop;
end;
Stmts := New_Stmts;
end Unnest_Statement_List;
--------------------------------
-- Wrap_Transient_Declaration --
--------------------------------