From 057469588864b2566f89e3a548a80d7517b34c98 Mon Sep 17 00:00:00 2001 From: Gary Dismukes Date: Wed, 24 Jun 2020 17:22:58 -0400 Subject: [PATCH] [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. --- gcc/ada/exp_ch7.adb | 198 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/gcc/ada/exp_ch7.adb b/gcc/ada/exp_ch7.adb index 07640bf6165..6e9266a2b56 100644 --- a/gcc/ada/exp_ch7.adb +++ b/gcc/ada/exp_ch7.adb @@ -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 -- --------------------------------