1------------------------------------------------------------------------------
2--                                                                          --
3--                         GNAT COMPILER COMPONENTS                         --
4--                                                                          --
5--                             E X P _ S M E M                              --
6--                                                                          --
7--                                 B o d y                                  --
8--                                                                          --
9--          Copyright (C) 1998-2014, Free Software Foundation, Inc.         --
10--                                                                          --
11-- GNAT is free software;  you can  redistribute it  and/or modify it under --
12-- terms of the  GNU General Public License as published  by the Free Soft- --
13-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
14-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
17-- for  more details.  You should have  received  a copy of the GNU General --
18-- Public License  distributed with GNAT; see file COPYING3.  If not, go to --
19-- http://www.gnu.org/licenses for a complete copy of the license.          --
20--                                                                          --
21-- GNAT was originally developed  by the GNAT team at  New York University. --
22-- Extensive contributions were provided by Ada Core Technologies Inc.      --
23--                                                                          --
24------------------------------------------------------------------------------
25
26with Atree;    use Atree;
27with Einfo;    use Einfo;
28with Elists;   use Elists;
29with Exp_Ch7;  use Exp_Ch7;
30with Exp_Ch9;  use Exp_Ch9;
31with Exp_Tss;  use Exp_Tss;
32with Exp_Util; use Exp_Util;
33with Nmake;    use Nmake;
34with Namet;    use Namet;
35with Nlists;   use Nlists;
36with Rtsfind;  use Rtsfind;
37with Sem;      use Sem;
38with Sem_Aux;  use Sem_Aux;
39with Sem_Util; use Sem_Util;
40with Sinfo;    use Sinfo;
41with Snames;   use Snames;
42with Stand;    use Stand;
43with Stringt;  use Stringt;
44with Tbuild;   use Tbuild;
45
46package body Exp_Smem is
47
48   Insert_Node : Node_Id;
49   --  Node after which a write call is to be inserted
50
51   -----------------------
52   -- Local Subprograms --
53   -----------------------
54
55   procedure Add_Read (N : Node_Id; Call : Node_Id := Empty);
56   --  Insert a Shared_Var_ROpen call for variable before node N, unless
57   --  Call is a call to an init-proc, in which case the call is inserted
58   --  after Call.
59
60   procedure Add_Write_After (N : Node_Id);
61   --  Insert a Shared_Var_WOpen call for variable after the node Insert_Node,
62   --  as recorded by On_Lhs_Of_Assignment (where it points to the assignment
63   --  statement) or Is_Out_Actual (where it points to the subprogram call).
64   --  When Insert_Node is a function call, establish a transient scope around
65   --  the expression, and insert the write as an after-action of the transient
66   --  scope.
67
68   procedure Build_Full_Name (E : Entity_Id; N : out String_Id);
69   --  Build the fully qualified string name of a shared variable
70
71   function On_Lhs_Of_Assignment (N : Node_Id) return Boolean;
72   --  Determines if N is on the left hand of the assignment. This means that
73   --  either it is a simple variable, or it is a record or array variable with
74   --  a corresponding selected or indexed component on the left side of an
75   --  assignment. If the result is True, then Insert_Node is set to point
76   --  to the assignment
77
78   function Is_Out_Actual (N : Node_Id) return Boolean;
79   --  In a similar manner, this function determines if N appears as an OUT
80   --  or IN OUT parameter to a procedure call. If the result is True, then
81   --  Insert_Node is set to point to the call.
82
83   function Build_Shared_Var_Proc_Call
84     (Loc : Source_Ptr;
85      E   : Node_Id;
86      N   : Name_Id) return Node_Id;
87   --  Build a call to support procedure N for shared object E (provided by the
88   --  instance of System.Shared_Storage.Shared_Var_Procs associated to E).
89
90   --------------------------------
91   -- Build_Shared_Var_Proc_Call --
92   --------------------------------
93
94   function Build_Shared_Var_Proc_Call
95     (Loc : Source_Ptr;
96      E   : Entity_Id;
97      N   : Name_Id) return Node_Id
98   is
99   begin
100      return Make_Procedure_Call_Statement (Loc,
101        Name => Make_Selected_Component (Loc,
102          Prefix        =>
103            New_Occurrence_Of (Shared_Var_Procs_Instance (E), Loc),
104          Selector_Name => Make_Identifier (Loc, N)));
105   end Build_Shared_Var_Proc_Call;
106
107   --------------
108   -- Add_Read --
109   --------------
110
111   procedure Add_Read (N : Node_Id; Call : Node_Id := Empty) is
112      Loc : constant Source_Ptr := Sloc (N);
113      Ent : constant Node_Id    := Entity (N);
114      SVC : Node_Id;
115
116   begin
117      if Present (Shared_Var_Procs_Instance (Ent)) then
118         SVC := Build_Shared_Var_Proc_Call (Loc, Ent, Name_Read);
119
120         if Present (Call) and then Is_Init_Proc (Name (Call)) then
121            Insert_After_And_Analyze (Call, SVC);
122         else
123            Insert_Action (N, SVC);
124         end if;
125      end if;
126   end Add_Read;
127
128   -------------------------------
129   -- Add_Shared_Var_Lock_Procs --
130   -------------------------------
131
132   procedure Add_Shared_Var_Lock_Procs (N : Node_Id) is
133      Loc : constant Source_Ptr := Sloc (N);
134      Obj : constant Entity_Id  := Entity (Expression (First_Actual (N)));
135      Vnm : String_Id;
136      Vid : Entity_Id;
137      Vde : Node_Id;
138      Aft : constant List_Id := New_List;
139
140      In_Transient : constant Boolean := Scope_Is_Transient;
141
142      function Build_Shared_Var_Lock_Call (RE : RE_Id) return Node_Id;
143      --  Return a procedure call statement for lock proc RTE
144
145      --------------------------------
146      -- Build_Shared_Var_Lock_Call --
147      --------------------------------
148
149      function Build_Shared_Var_Lock_Call (RE : RE_Id) return Node_Id is
150      begin
151         return
152           Make_Procedure_Call_Statement (Loc,
153             Name                   =>
154               New_Occurrence_Of (RTE (RE), Loc),
155             Parameter_Associations =>
156               New_List (New_Occurrence_Of (Vid, Loc)));
157      end Build_Shared_Var_Lock_Call;
158
159   --  Start of processing for Add_Shared_Var_Lock_Procs
160
161   begin
162      --  Discussion of transient scopes: we need to have a transient scope
163      --  to hold the required lock/unlock actions. Either the current scope
164      --  is transient, in which case we reuse it, or we establish a new
165      --  transient scope. If this is a function call with unconstrained
166      --  return type, we can't introduce a transient scope here (because
167      --  Wrap_Transient_Expression would need to declare a temporary with
168      --  the unconstrained type outside of the transient block), but in that
169      --  case we know that we have already established one at an outer level
170      --  for secondary stack management purposes.
171
172      --  If the lock/read/write/unlock actions for this object have already
173      --  been emitted in the current scope, no need to perform them anew.
174
175      if In_Transient
176        and then Contains (Scope_Stack.Table (Scope_Stack.Last)
177                             .Locked_Shared_Objects,
178                           Obj)
179      then
180         return;
181      end if;
182
183      Build_Full_Name (Obj, Vnm);
184
185      --  Declare a constant string to hold the name of the shared object.
186      --  Note that this must occur outside of the transient scope, as the
187      --  scope's finalizer needs to have access to this object. Also, it
188      --  appears that GIGI does not support elaborating string literal
189      --  subtypes in transient scopes.
190
191      Vid := Make_Temporary (Loc, 'N', Obj);
192      Vde :=
193        Make_Object_Declaration (Loc,
194          Defining_Identifier => Vid,
195          Constant_Present    => True,
196          Object_Definition   => New_Occurrence_Of (Standard_String, Loc),
197          Expression          => Make_String_Literal (Loc, Vnm));
198
199      --  Already in a transient scope. Make sure that we insert Vde outside
200      --  that scope.
201
202      if In_Transient then
203         Insert_Before_And_Analyze (Node_To_Be_Wrapped, Vde);
204
205      --  Not in a transient scope yet: insert Vde as an action on N prior to
206      --  establishing one.
207
208      else
209         Insert_Action (N, Vde);
210         Establish_Transient_Scope (N, Sec_Stack => False);
211      end if;
212
213      --  Mark object as locked in the current (transient) scope
214
215      Append_New_Elmt
216        (Obj,
217         To => Scope_Stack.Table (Scope_Stack.Last).Locked_Shared_Objects);
218
219      --  First insert the Lock call before
220
221      Insert_Action (N, Build_Shared_Var_Lock_Call (RE_Shared_Var_Lock));
222
223      --  Now, right after the Lock, insert a call to read the object
224
225      Insert_Action (N, Build_Shared_Var_Proc_Call (Loc, Obj, Name_Read));
226
227      --  For a procedure call only, insert the call to write the object prior
228      --  to unlocking.
229
230      if Nkind (N) = N_Procedure_Call_Statement then
231         Append_To (Aft, Build_Shared_Var_Proc_Call (Loc, Obj, Name_Write));
232      end if;
233
234      --  Finally insert the Unlock call
235
236      Append_To (Aft, Build_Shared_Var_Lock_Call (RE_Shared_Var_Unlock));
237
238      --  Store cleanup actions in transient scope
239
240      Store_Cleanup_Actions_In_Scope (Aft);
241
242      --  If we have established a transient scope here, wrap it now
243
244      if not In_Transient then
245         if Nkind (N) = N_Procedure_Call_Statement then
246            Wrap_Transient_Statement (N);
247         else
248            Wrap_Transient_Expression (N);
249         end if;
250      end if;
251   end Add_Shared_Var_Lock_Procs;
252
253   ---------------------
254   -- Add_Write_After --
255   ---------------------
256
257   procedure Add_Write_After (N : Node_Id) is
258      Loc : constant Source_Ptr := Sloc (N);
259      Ent : constant Entity_Id  := Entity (N);
260      Par : constant Node_Id    := Insert_Node;
261   begin
262      if Present (Shared_Var_Procs_Instance (Ent)) then
263         if Nkind (Insert_Node) = N_Function_Call then
264            Establish_Transient_Scope (Insert_Node, Sec_Stack => False);
265            Store_After_Actions_In_Scope (New_List (
266              Build_Shared_Var_Proc_Call (Loc, Ent, Name_Write)));
267         else
268            Insert_After_And_Analyze (Par,
269              Build_Shared_Var_Proc_Call (Loc, Ent, Name_Write));
270         end if;
271      end if;
272   end Add_Write_After;
273
274   ---------------------
275   -- Build_Full_Name --
276   ---------------------
277
278   procedure Build_Full_Name (E : Entity_Id; N : out String_Id) is
279
280      procedure Build_Name (E : Entity_Id);
281      --  This is a recursive routine used to construct the fully qualified
282      --  string name of the package corresponding to the shared variable.
283
284      ----------------
285      -- Build_Name --
286      ----------------
287
288      procedure Build_Name (E : Entity_Id) is
289      begin
290         if Scope (E) /= Standard_Standard then
291            Build_Name (Scope (E));
292            Store_String_Char ('.');
293         end if;
294
295         Get_Decoded_Name_String (Chars (E));
296         Store_String_Chars (Name_Buffer (1 .. Name_Len));
297      end Build_Name;
298
299   --  Start of processing for Build_Full_Name
300
301   begin
302      Start_String;
303      Build_Name (E);
304      N := End_String;
305   end Build_Full_Name;
306
307   ------------------------------------
308   -- Expand_Shared_Passive_Variable --
309   ------------------------------------
310
311   procedure Expand_Shared_Passive_Variable (N : Node_Id) is
312      Typ : constant Entity_Id := Etype (N);
313
314   begin
315      --  Nothing to do for protected or limited objects
316
317      if Is_Limited_Type (Typ) or else Is_Concurrent_Type (Typ) then
318         return;
319
320      --  If we are on the left hand side of an assignment, then we add the
321      --  write call after the assignment.
322
323      elsif On_Lhs_Of_Assignment (N) then
324         Add_Write_After (N);
325
326      --  If we are a parameter for an out or in out formal, then in general
327      --  we do:
328
329      --    read
330      --    call
331      --    write
332
333      --  but in the special case of a call to an init proc, we need to first
334      --  call the init proc (to set discriminants), then read (to possibly
335      --  set other components), then write (to record the updated components
336      --  to the backing store):
337
338      --    init-proc-call
339      --    read
340      --    write
341
342      elsif Is_Out_Actual (N) then
343
344         --  Note: For an init proc call, Add_Read inserts just after the
345         --  call node, and we want to have first the read, then the write,
346         --  so we need to first Add_Write_After, then Add_Read.
347
348         Add_Write_After (N);
349         Add_Read (N, Call => Insert_Node);
350
351      --  All other cases are simple reads
352
353      else
354         Add_Read (N);
355      end if;
356   end Expand_Shared_Passive_Variable;
357
358   -------------------
359   -- Is_Out_Actual --
360   -------------------
361
362   function Is_Out_Actual (N : Node_Id) return Boolean is
363      Formal : Entity_Id;
364      Call   : Node_Id;
365
366   begin
367      Find_Actual (N, Formal, Call);
368
369      if No (Formal) then
370         return False;
371
372      else
373         if Ekind_In (Formal, E_Out_Parameter, E_In_Out_Parameter) then
374            Insert_Node := Call;
375            return True;
376         else
377            return False;
378         end if;
379      end if;
380   end Is_Out_Actual;
381
382   ---------------------------
383   -- Make_Shared_Var_Procs --
384   ---------------------------
385
386   function Make_Shared_Var_Procs (N : Node_Id) return Node_Id is
387      Loc     : constant Source_Ptr := Sloc (N);
388      Ent     : constant Entity_Id  := Defining_Identifier (N);
389      Typ     : constant Entity_Id  := Etype (Ent);
390      Vnm     : String_Id;
391      Obj     : Node_Id;
392      Obj_Typ : Entity_Id;
393
394      After : constant Node_Id := Next (N);
395      --  Node located right after N originally (after insertion of the SV
396      --  procs this node is right after the last inserted node).
397
398      SVP_Instance : constant Entity_Id := Make_Defining_Identifier (Loc,
399                       Chars => New_External_Name (Chars (Ent), 'G'));
400      --  Instance of Shared_Storage.Shared_Var_Procs associated with Ent
401
402      Instantiation : Node_Id;
403      --  Package instantiation node for SVP_Instance
404
405   --  Start of processing for Make_Shared_Var_Procs
406
407   begin
408      Build_Full_Name (Ent, Vnm);
409
410      --  We turn off Shared_Passive during construction and analysis of the
411      --  generic package instantiation, to avoid improper attempts to process
412      --  the variable references within these instantiation.
413
414      Set_Is_Shared_Passive (Ent, False);
415
416      --  Construct generic package instantiation
417
418      --  package varG is new Shared_Var_Procs (typ, var, "pkg.var");
419
420      Obj     := New_Occurrence_Of (Ent, Loc);
421      Obj_Typ := Typ;
422      if Is_Concurrent_Type (Typ) then
423         Obj     := Convert_Concurrent (N => Obj, Typ => Typ);
424         Obj_Typ := Corresponding_Record_Type (Typ);
425      end if;
426
427      Instantiation :=
428        Make_Package_Instantiation (Loc,
429          Defining_Unit_Name   => SVP_Instance,
430          Name                 =>
431            New_Occurrence_Of (RTE (RE_Shared_Var_Procs), Loc),
432          Generic_Associations => New_List (
433            Make_Generic_Association (Loc,
434              Explicit_Generic_Actual_Parameter =>
435                New_Occurrence_Of (Obj_Typ, Loc)),
436            Make_Generic_Association (Loc,
437              Explicit_Generic_Actual_Parameter => Obj),
438            Make_Generic_Association (Loc,
439              Explicit_Generic_Actual_Parameter =>
440                Make_String_Literal (Loc, Vnm))));
441
442      Insert_After_And_Analyze (N, Instantiation);
443
444      Set_Is_Shared_Passive (Ent, True);
445      Set_Shared_Var_Procs_Instance
446        (Ent, Defining_Entity (Instance_Spec (Instantiation)));
447
448      --  Return last node before After
449
450      declare
451         Nod : Node_Id := Next (N);
452
453      begin
454         while Next (Nod) /= After loop
455            Nod := Next (Nod);
456         end loop;
457
458         return Nod;
459      end;
460   end Make_Shared_Var_Procs;
461
462   --------------------------
463   -- On_Lhs_Of_Assignment --
464   --------------------------
465
466   function On_Lhs_Of_Assignment (N : Node_Id) return Boolean is
467      P : constant Node_Id := Parent (N);
468
469   begin
470      if Nkind (P) = N_Assignment_Statement then
471         if N = Name (P) then
472            Insert_Node := P;
473            return True;
474         else
475            return False;
476         end if;
477
478      elsif Nkind_In (P, N_Indexed_Component, N_Selected_Component)
479        and then N = Prefix (P)
480      then
481         return On_Lhs_Of_Assignment (P);
482
483      else
484         return False;
485      end if;
486   end On_Lhs_Of_Assignment;
487
488end Exp_Smem;
489