| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- L A Y O U T -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2001-2003 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 2, 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 COPYING. If not, write -- |
| -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- |
| -- MA 02111-1307, USA. -- |
| -- -- |
| -- 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 Checks; use Checks; |
| with Debug; use Debug; |
| with Einfo; use Einfo; |
| with Errout; use Errout; |
| with Exp_Ch3; use Exp_Ch3; |
| with Exp_Util; use Exp_Util; |
| with Nlists; use Nlists; |
| with Nmake; use Nmake; |
| with Opt; use Opt; |
| with Repinfo; use Repinfo; |
| with Sem; use Sem; |
| with Sem_Ch13; use Sem_Ch13; |
| with Sem_Eval; use Sem_Eval; |
| with Sem_Util; use Sem_Util; |
| with Sinfo; use Sinfo; |
| with Snames; use Snames; |
| with Stand; use Stand; |
| with Targparm; use Targparm; |
| with Tbuild; use Tbuild; |
| with Ttypes; use Ttypes; |
| with Uintp; use Uintp; |
| |
| package body Layout is |
| |
| ------------------------ |
| -- Local Declarations -- |
| ------------------------ |
| |
| SSU : constant Int := Ttypes.System_Storage_Unit; |
| -- Short hand for System_Storage_Unit |
| |
| Vname : constant Name_Id := Name_uV; |
| -- Formal parameter name used for functions generated for size offset |
| -- values that depend on the discriminant. All such functions have the |
| -- following form: |
| -- |
| -- function xxx (V : vtyp) return Unsigned is |
| -- begin |
| -- return ... expression involving V.discrim |
| -- end xxx; |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| procedure Adjust_Esize_Alignment (E : Entity_Id); |
| -- E is the entity for a type or object. This procedure checks that the |
| -- size and alignment are compatible, and if not either gives an error |
| -- message if they cannot be adjusted or else adjusts them appropriately. |
| |
| function Assoc_Add |
| (Loc : Source_Ptr; |
| Left_Opnd : Node_Id; |
| Right_Opnd : Node_Id) |
| return Node_Id; |
| -- This is like Make_Op_Add except that it optimizes some cases knowing |
| -- that associative rearrangement is allowed for constant folding if one |
| -- of the operands is a compile time known value. |
| |
| function Assoc_Multiply |
| (Loc : Source_Ptr; |
| Left_Opnd : Node_Id; |
| Right_Opnd : Node_Id) |
| return Node_Id; |
| -- This is like Make_Op_Multiply except that it optimizes some cases |
| -- knowing that associative rearrangement is allowed for constant |
| -- folding if one of the operands is a compile time known value |
| |
| function Assoc_Subtract |
| (Loc : Source_Ptr; |
| Left_Opnd : Node_Id; |
| Right_Opnd : Node_Id) |
| return Node_Id; |
| -- This is like Make_Op_Subtract except that it optimizes some cases |
| -- knowing that associative rearrangement is allowed for constant |
| -- folding if one of the operands is a compile time known value |
| |
| function Bits_To_SU (N : Node_Id) return Node_Id; |
| -- This is used when we cross the boundary from static sizes in bits to |
| -- dynamic sizes in storage units. If the argument N is anything other |
| -- than an integer literal, it is returned unchanged, but if it is an |
| -- integer literal, then it is taken as a size in bits, and is replaced |
| -- by the corresponding size in bytes. |
| |
| function Compute_Length (Lo : Node_Id; Hi : Node_Id) return Node_Id; |
| -- Given expressions for the low bound (Lo) and the high bound (Hi), |
| -- Build an expression for the value hi-lo+1, converted to type |
| -- Standard.Unsigned. Takes care of the case where the operands |
| -- are of an enumeration type (so that the subtraction cannot be |
| -- done directly) by applying the Pos operator to Hi/Lo first. |
| |
| function Expr_From_SO_Ref |
| (Loc : Source_Ptr; |
| D : SO_Ref; |
| Comp : Entity_Id := Empty) |
| return Node_Id; |
| -- Given a value D from a size or offset field, return an expression |
| -- representing the value stored. If the value is known at compile time, |
| -- then an N_Integer_Literal is returned with the appropriate value. If |
| -- the value references a constant entity, then an N_Identifier node |
| -- referencing this entity is returned. If the value denotes a size |
| -- function, then returns a call node denoting the given function, with |
| -- a single actual parameter that either refers to the parameter V of |
| -- an enclosing size function (if Comp is Empty or its type doesn't match |
| -- the function's formal), or else is a selected component V.c when Comp |
| -- denotes a component c whose type matches that of the function formal. |
| -- The Loc value is used for the Sloc value of constructed notes. |
| |
| function SO_Ref_From_Expr |
| (Expr : Node_Id; |
| Ins_Type : Entity_Id; |
| Vtype : Entity_Id := Empty; |
| Make_Func : Boolean := False) |
| return Dynamic_SO_Ref; |
| -- This routine is used in the case where a size/offset value is dynamic |
| -- and is represented by the expression Expr. SO_Ref_From_Expr checks if |
| -- the Expr contains a reference to the identifier V, and if so builds |
| -- a function depending on discriminants of the formal parameter V which |
| -- is of type Vtype. Otherwise, if the parameter Make_Func is True, then |
| -- Expr will be encapsulated in a parameterless function; if Make_Func is |
| -- False, then a constant entity with the value Expr is built. The result |
| -- is a Dynamic_SO_Ref to the created entity. Note that Vtype can be |
| -- omitted if Expr does not contain any reference to V, the created entity. |
| -- The declaration created is inserted in the freeze actions of Ins_Type, |
| -- which also supplies the Sloc for created nodes. This function also takes |
| -- care of making sure that the expression is properly analyzed and |
| -- resolved (which may not be the case yet if we build the expression |
| -- in this unit). |
| |
| function Get_Max_Size (E : Entity_Id) return Node_Id; |
| -- E is an array type or subtype that has at least one index bound that |
| -- is the value of a record discriminant. For such an array, the function |
| -- computes an expression that yields the maximum possible size of the |
| -- array in storage units. The result is not defined for any other type, |
| -- or for arrays that do not depend on discriminants, and it is a fatal |
| -- error to call this unless Size_Depends_On_Discriminant (E) is True. |
| |
| procedure Layout_Array_Type (E : Entity_Id); |
| -- Front-end layout of non-bit-packed array type or subtype |
| |
| procedure Layout_Record_Type (E : Entity_Id); |
| -- Front-end layout of record type |
| |
| procedure Rewrite_Integer (N : Node_Id; V : Uint); |
| -- Rewrite node N with an integer literal whose value is V. The Sloc |
| -- for the new node is taken from N, and the type of the literal is |
| -- set to a copy of the type of N on entry. |
| |
| procedure Set_And_Check_Static_Size |
| (E : Entity_Id; |
| Esiz : SO_Ref; |
| RM_Siz : SO_Ref); |
| -- This procedure is called to check explicit given sizes (possibly |
| -- stored in the Esize and RM_Size fields of E) against computed |
| -- Object_Size (Esiz) and Value_Size (RM_Siz) values. Appropriate |
| -- errors and warnings are posted if specified sizes are inconsistent |
| -- with specified sizes. On return, the Esize and RM_Size fields of |
| -- E are set (either from previously given values, or from the newly |
| -- computed values, as appropriate). |
| |
| procedure Set_Composite_Alignment (E : Entity_Id); |
| -- This procedure is called for record types and subtypes, and also for |
| -- atomic array types and subtypes. If no alignment is set, and the size |
| -- is 2 or 4 (or 8 if the word size is 8), then the alignment is set to |
| -- match the size. |
| |
| ---------------------------- |
| -- Adjust_Esize_Alignment -- |
| ---------------------------- |
| |
| procedure Adjust_Esize_Alignment (E : Entity_Id) is |
| Abits : Int; |
| Esize_Set : Boolean; |
| |
| begin |
| -- Nothing to do if size unknown |
| |
| if Unknown_Esize (E) then |
| return; |
| end if; |
| |
| -- Determine if size is constrained by an attribute definition clause |
| -- which must be obeyed. If so, we cannot increase the size in this |
| -- routine. |
| |
| -- For a type, the issue is whether an object size clause has been |
| -- set. A normal size clause constrains only the value size (RM_Size) |
| |
| if Is_Type (E) then |
| Esize_Set := Has_Object_Size_Clause (E); |
| |
| -- For an object, the issue is whether a size clause is present |
| |
| else |
| Esize_Set := Has_Size_Clause (E); |
| end if; |
| |
| -- If size is known it must be a multiple of the byte size |
| |
| if Esize (E) mod SSU /= 0 then |
| |
| -- If not, and size specified, then give error |
| |
| if Esize_Set then |
| Error_Msg_NE |
| ("size for& not a multiple of byte size", Size_Clause (E), E); |
| return; |
| |
| -- Otherwise bump up size to a byte boundary |
| |
| else |
| Set_Esize (E, (Esize (E) + SSU - 1) / SSU * SSU); |
| end if; |
| end if; |
| |
| -- Now we have the size set, it must be a multiple of the alignment |
| -- nothing more we can do here if the alignment is unknown here. |
| |
| if Unknown_Alignment (E) then |
| return; |
| end if; |
| |
| -- At this point both the Esize and Alignment are known, so we need |
| -- to make sure they are consistent. |
| |
| Abits := UI_To_Int (Alignment (E)) * SSU; |
| |
| if Esize (E) mod Abits = 0 then |
| return; |
| end if; |
| |
| -- Here we have a situation where the Esize is not a multiple of |
| -- the alignment. We must either increase Esize or reduce the |
| -- alignment to correct this situation. |
| |
| -- The case in which we can decrease the alignment is where the |
| -- alignment was not set by an alignment clause, and the type in |
| -- question is a discrete type, where it is definitely safe to |
| -- reduce the alignment. For example: |
| |
| -- t : integer range 1 .. 2; |
| -- for t'size use 8; |
| |
| -- In this situation, the initial alignment of t is 4, copied from |
| -- the Integer base type, but it is safe to reduce it to 1 at this |
| -- stage, since we will only be loading a single byte. |
| |
| if Is_Discrete_Type (Etype (E)) |
| and then not Has_Alignment_Clause (E) |
| then |
| loop |
| Abits := Abits / 2; |
| exit when Esize (E) mod Abits = 0; |
| end loop; |
| |
| Init_Alignment (E, Abits / SSU); |
| return; |
| end if; |
| |
| -- Now the only possible approach left is to increase the Esize |
| -- but we can't do that if the size was set by a specific clause. |
| |
| if Esize_Set then |
| Error_Msg_NE |
| ("size for& is not a multiple of alignment", |
| Size_Clause (E), E); |
| |
| -- Otherwise we can indeed increase the size to a multiple of alignment |
| |
| else |
| Set_Esize (E, ((Esize (E) + (Abits - 1)) / Abits) * Abits); |
| end if; |
| end Adjust_Esize_Alignment; |
| |
| --------------- |
| -- Assoc_Add -- |
| --------------- |
| |
| function Assoc_Add |
| (Loc : Source_Ptr; |
| Left_Opnd : Node_Id; |
| Right_Opnd : Node_Id) |
| return Node_Id |
| is |
| L : Node_Id; |
| R : Uint; |
| |
| begin |
| -- Case of right operand is a constant |
| |
| if Compile_Time_Known_Value (Right_Opnd) then |
| L := Left_Opnd; |
| R := Expr_Value (Right_Opnd); |
| |
| -- Case of left operand is a constant |
| |
| elsif Compile_Time_Known_Value (Left_Opnd) then |
| L := Right_Opnd; |
| R := Expr_Value (Left_Opnd); |
| |
| -- Neither operand is a constant, do the addition with no optimization |
| |
| else |
| return Make_Op_Add (Loc, Left_Opnd, Right_Opnd); |
| end if; |
| |
| -- Case of left operand is an addition |
| |
| if Nkind (L) = N_Op_Add then |
| |
| -- (C1 + E) + C2 = (C1 + C2) + E |
| |
| if Compile_Time_Known_Value (Sinfo.Left_Opnd (L)) then |
| Rewrite_Integer |
| (Sinfo.Left_Opnd (L), |
| Expr_Value (Sinfo.Left_Opnd (L)) + R); |
| return L; |
| |
| -- (E + C1) + C2 = E + (C1 + C2) |
| |
| elsif Compile_Time_Known_Value (Sinfo.Right_Opnd (L)) then |
| Rewrite_Integer |
| (Sinfo.Right_Opnd (L), |
| Expr_Value (Sinfo.Right_Opnd (L)) + R); |
| return L; |
| end if; |
| |
| -- Case of left operand is a subtraction |
| |
| elsif Nkind (L) = N_Op_Subtract then |
| |
| -- (C1 - E) + C2 = (C1 + C2) + E |
| |
| if Compile_Time_Known_Value (Sinfo.Left_Opnd (L)) then |
| Rewrite_Integer |
| (Sinfo.Left_Opnd (L), |
| Expr_Value (Sinfo.Left_Opnd (L)) + R); |
| return L; |
| |
| -- (E - C1) + C2 = E - (C1 - C2) |
| |
| elsif Compile_Time_Known_Value (Sinfo.Right_Opnd (L)) then |
| Rewrite_Integer |
| (Sinfo.Right_Opnd (L), |
| Expr_Value (Sinfo.Right_Opnd (L)) - R); |
| return L; |
| end if; |
| end if; |
| |
| -- Not optimizable, do the addition |
| |
| return Make_Op_Add (Loc, Left_Opnd, Right_Opnd); |
| end Assoc_Add; |
| |
| -------------------- |
| -- Assoc_Multiply -- |
| -------------------- |
| |
| function Assoc_Multiply |
| (Loc : Source_Ptr; |
| Left_Opnd : Node_Id; |
| Right_Opnd : Node_Id) |
| return Node_Id |
| is |
| L : Node_Id; |
| R : Uint; |
| |
| begin |
| -- Case of right operand is a constant |
| |
| if Compile_Time_Known_Value (Right_Opnd) then |
| L := Left_Opnd; |
| R := Expr_Value (Right_Opnd); |
| |
| -- Case of left operand is a constant |
| |
| elsif Compile_Time_Known_Value (Left_Opnd) then |
| L := Right_Opnd; |
| R := Expr_Value (Left_Opnd); |
| |
| -- Neither operand is a constant, do the multiply with no optimization |
| |
| else |
| return Make_Op_Multiply (Loc, Left_Opnd, Right_Opnd); |
| end if; |
| |
| -- Case of left operand is an multiplication |
| |
| if Nkind (L) = N_Op_Multiply then |
| |
| -- (C1 * E) * C2 = (C1 * C2) + E |
| |
| if Compile_Time_Known_Value (Sinfo.Left_Opnd (L)) then |
| Rewrite_Integer |
| (Sinfo.Left_Opnd (L), |
| Expr_Value (Sinfo.Left_Opnd (L)) * R); |
| return L; |
| |
| -- (E * C1) * C2 = E * (C1 * C2) |
| |
| elsif Compile_Time_Known_Value (Sinfo.Right_Opnd (L)) then |
| Rewrite_Integer |
| (Sinfo.Right_Opnd (L), |
| Expr_Value (Sinfo.Right_Opnd (L)) * R); |
| return L; |
| end if; |
| end if; |
| |
| -- Not optimizable, do the multiplication |
| |
| return Make_Op_Multiply (Loc, Left_Opnd, Right_Opnd); |
| end Assoc_Multiply; |
| |
| -------------------- |
| -- Assoc_Subtract -- |
| -------------------- |
| |
| function Assoc_Subtract |
| (Loc : Source_Ptr; |
| Left_Opnd : Node_Id; |
| Right_Opnd : Node_Id) |
| return Node_Id |
| is |
| L : Node_Id; |
| R : Uint; |
| |
| begin |
| -- Case of right operand is a constant |
| |
| if Compile_Time_Known_Value (Right_Opnd) then |
| L := Left_Opnd; |
| R := Expr_Value (Right_Opnd); |
| |
| -- Right operand is a constant, do the subtract with no optimization |
| |
| else |
| return Make_Op_Subtract (Loc, Left_Opnd, Right_Opnd); |
| end if; |
| |
| -- Case of left operand is an addition |
| |
| if Nkind (L) = N_Op_Add then |
| |
| -- (C1 + E) - C2 = (C1 - C2) + E |
| |
| if Compile_Time_Known_Value (Sinfo.Left_Opnd (L)) then |
| Rewrite_Integer |
| (Sinfo.Left_Opnd (L), |
| Expr_Value (Sinfo.Left_Opnd (L)) - R); |
| return L; |
| |
| -- (E + C1) - C2 = E + (C1 - C2) |
| |
| elsif Compile_Time_Known_Value (Sinfo.Right_Opnd (L)) then |
| Rewrite_Integer |
| (Sinfo.Right_Opnd (L), |
| Expr_Value (Sinfo.Right_Opnd (L)) - R); |
| return L; |
| end if; |
| |
| -- Case of left operand is a subtraction |
| |
| elsif Nkind (L) = N_Op_Subtract then |
| |
| -- (C1 - E) - C2 = (C1 - C2) + E |
| |
| if Compile_Time_Known_Value (Sinfo.Left_Opnd (L)) then |
| Rewrite_Integer |
| (Sinfo.Left_Opnd (L), |
| Expr_Value (Sinfo.Left_Opnd (L)) + R); |
| return L; |
| |
| -- (E - C1) - C2 = E - (C1 + C2) |
| |
| elsif Compile_Time_Known_Value (Sinfo.Right_Opnd (L)) then |
| Rewrite_Integer |
| (Sinfo.Right_Opnd (L), |
| Expr_Value (Sinfo.Right_Opnd (L)) + R); |
| return L; |
| end if; |
| end if; |
| |
| -- Not optimizable, do the subtraction |
| |
| return Make_Op_Subtract (Loc, Left_Opnd, Right_Opnd); |
| end Assoc_Subtract; |
| |
| ---------------- |
| -- Bits_To_SU -- |
| ---------------- |
| |
| function Bits_To_SU (N : Node_Id) return Node_Id is |
| begin |
| if Nkind (N) = N_Integer_Literal then |
| Set_Intval (N, (Intval (N) + (SSU - 1)) / SSU); |
| end if; |
| |
| return N; |
| end Bits_To_SU; |
| |
| -------------------- |
| -- Compute_Length -- |
| -------------------- |
| |
| function Compute_Length (Lo : Node_Id; Hi : Node_Id) return Node_Id is |
| Loc : constant Source_Ptr := Sloc (Lo); |
| Typ : constant Entity_Id := Etype (Lo); |
| Lo_Op : Node_Id; |
| Hi_Op : Node_Id; |
| Lo_Dim : Uint; |
| Hi_Dim : Uint; |
| |
| begin |
| -- If the bounds are First and Last attributes for the same dimension |
| -- and both have prefixes that denotes the same entity, then we create |
| -- and return a Length attribute. This may allow the back end to |
| -- generate better code in cases where it already has the length. |
| |
| if Nkind (Lo) = N_Attribute_Reference |
| and then Attribute_Name (Lo) = Name_First |
| and then Nkind (Hi) = N_Attribute_Reference |
| and then Attribute_Name (Hi) = Name_Last |
| and then Is_Entity_Name (Prefix (Lo)) |
| and then Is_Entity_Name (Prefix (Hi)) |
| and then Entity (Prefix (Lo)) = Entity (Prefix (Hi)) |
| then |
| Lo_Dim := Uint_1; |
| Hi_Dim := Uint_1; |
| |
| if Present (First (Expressions (Lo))) then |
| Lo_Dim := Expr_Value (First (Expressions (Lo))); |
| end if; |
| |
| if Present (First (Expressions (Hi))) then |
| Hi_Dim := Expr_Value (First (Expressions (Hi))); |
| end if; |
| |
| if Lo_Dim = Hi_Dim then |
| return |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of |
| (Entity (Prefix (Lo)), Loc), |
| Attribute_Name => Name_Length, |
| Expressions => New_List |
| (Make_Integer_Literal (Loc, Lo_Dim))); |
| end if; |
| end if; |
| |
| Lo_Op := New_Copy_Tree (Lo); |
| Hi_Op := New_Copy_Tree (Hi); |
| |
| -- If type is enumeration type, then use Pos attribute to convert |
| -- to integer type for which subtraction is a permitted operation. |
| |
| if Is_Enumeration_Type (Typ) then |
| Lo_Op := |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Typ, Loc), |
| Attribute_Name => Name_Pos, |
| Expressions => New_List (Lo_Op)); |
| |
| Hi_Op := |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Typ, Loc), |
| Attribute_Name => Name_Pos, |
| Expressions => New_List (Hi_Op)); |
| end if; |
| |
| return |
| Assoc_Add (Loc, |
| Left_Opnd => |
| Assoc_Subtract (Loc, |
| Left_Opnd => Hi_Op, |
| Right_Opnd => Lo_Op), |
| Right_Opnd => Make_Integer_Literal (Loc, 1)); |
| end Compute_Length; |
| |
| ---------------------- |
| -- Expr_From_SO_Ref -- |
| ---------------------- |
| |
| function Expr_From_SO_Ref |
| (Loc : Source_Ptr; |
| D : SO_Ref; |
| Comp : Entity_Id := Empty) |
| return Node_Id |
| is |
| Ent : Entity_Id; |
| |
| begin |
| if Is_Dynamic_SO_Ref (D) then |
| Ent := Get_Dynamic_SO_Entity (D); |
| |
| if Is_Discrim_SO_Function (Ent) then |
| -- If a component is passed in whose type matches the type |
| -- of the function formal, then select that component from |
| -- the "V" parameter rather than passing "V" directly. |
| |
| if Present (Comp) |
| and then Base_Type (Etype (Comp)) |
| = Base_Type (Etype (First_Formal (Ent))) |
| then |
| return |
| Make_Function_Call (Loc, |
| Name => New_Occurrence_Of (Ent, Loc), |
| Parameter_Associations => New_List ( |
| Make_Selected_Component (Loc, |
| Prefix => Make_Identifier (Loc, Chars => Vname), |
| Selector_Name => New_Occurrence_Of (Comp, Loc)))); |
| |
| else |
| return |
| Make_Function_Call (Loc, |
| Name => New_Occurrence_Of (Ent, Loc), |
| Parameter_Associations => New_List ( |
| Make_Identifier (Loc, Chars => Vname))); |
| end if; |
| |
| else |
| return New_Occurrence_Of (Ent, Loc); |
| end if; |
| |
| else |
| return Make_Integer_Literal (Loc, D); |
| end if; |
| end Expr_From_SO_Ref; |
| |
| ------------------ |
| -- Get_Max_Size -- |
| ------------------ |
| |
| function Get_Max_Size (E : Entity_Id) return Node_Id is |
| Loc : constant Source_Ptr := Sloc (E); |
| Indx : Node_Id; |
| Ityp : Entity_Id; |
| Lo : Node_Id; |
| Hi : Node_Id; |
| S : Uint; |
| Len : Node_Id; |
| |
| type Val_Status_Type is (Const, Dynamic); |
| |
| type Val_Type (Status : Val_Status_Type := Const) is |
| record |
| case Status is |
| when Const => Val : Uint; |
| when Dynamic => Nod : Node_Id; |
| end case; |
| end record; |
| -- Shows the status of the value so far. Const means that the value |
| -- is constant, and Val is the current constant value. Dynamic means |
| -- that the value is dynamic, and in this case Nod is the Node_Id of |
| -- the expression to compute the value. |
| |
| Size : Val_Type; |
| -- Calculated value so far if Size.Status = Const, |
| -- or expression value so far if Size.Status = Dynamic. |
| |
| SU_Convert_Required : Boolean := False; |
| -- This is set to True if the final result must be converted from |
| -- bits to storage units (rounding up to a storage unit boundary). |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| procedure Max_Discrim (N : in out Node_Id); |
| -- If the node N represents a discriminant, replace it by the maximum |
| -- value of the discriminant. |
| |
| procedure Min_Discrim (N : in out Node_Id); |
| -- If the node N represents a discriminant, replace it by the minimum |
| -- value of the discriminant. |
| |
| ----------------- |
| -- Max_Discrim -- |
| ----------------- |
| |
| procedure Max_Discrim (N : in out Node_Id) is |
| begin |
| if Nkind (N) = N_Identifier |
| and then Ekind (Entity (N)) = E_Discriminant |
| then |
| N := Type_High_Bound (Etype (N)); |
| end if; |
| end Max_Discrim; |
| |
| ----------------- |
| -- Min_Discrim -- |
| ----------------- |
| |
| procedure Min_Discrim (N : in out Node_Id) is |
| begin |
| if Nkind (N) = N_Identifier |
| and then Ekind (Entity (N)) = E_Discriminant |
| then |
| N := Type_Low_Bound (Etype (N)); |
| end if; |
| end Min_Discrim; |
| |
| -- Start of processing for Get_Max_Size |
| |
| begin |
| pragma Assert (Size_Depends_On_Discriminant (E)); |
| |
| -- Initialize status from component size |
| |
| if Known_Static_Component_Size (E) then |
| Size := (Const, Component_Size (E)); |
| |
| else |
| Size := (Dynamic, Expr_From_SO_Ref (Loc, Component_Size (E))); |
| end if; |
| |
| -- Loop through indices |
| |
| Indx := First_Index (E); |
| while Present (Indx) loop |
| Ityp := Etype (Indx); |
| Lo := Type_Low_Bound (Ityp); |
| Hi := Type_High_Bound (Ityp); |
| |
| Min_Discrim (Lo); |
| Max_Discrim (Hi); |
| |
| -- Value of the current subscript range is statically known |
| |
| if Compile_Time_Known_Value (Lo) |
| and then Compile_Time_Known_Value (Hi) |
| then |
| S := Expr_Value (Hi) - Expr_Value (Lo) + 1; |
| |
| -- If known flat bound, entire size of array is zero! |
| |
| if S <= 0 then |
| return Make_Integer_Literal (Loc, 0); |
| end if; |
| |
| -- Current value is constant, evolve value |
| |
| if Size.Status = Const then |
| Size.Val := Size.Val * S; |
| |
| -- Current value is dynamic |
| |
| else |
| -- An interesting little optimization, if we have a pending |
| -- conversion from bits to storage units, and the current |
| -- length is a multiple of the storage unit size, then we |
| -- can take the factor out here statically, avoiding some |
| -- extra dynamic computations at the end. |
| |
| if SU_Convert_Required and then S mod SSU = 0 then |
| S := S / SSU; |
| SU_Convert_Required := False; |
| end if; |
| |
| Size.Nod := |
| Assoc_Multiply (Loc, |
| Left_Opnd => Size.Nod, |
| Right_Opnd => |
| Make_Integer_Literal (Loc, Intval => S)); |
| end if; |
| |
| -- Value of the current subscript range is dynamic |
| |
| else |
| -- If the current size value is constant, then here is where we |
| -- make a transition to dynamic values, which are always stored |
| -- in storage units, However, we do not want to convert to SU's |
| -- too soon, consider the case of a packed array of single bits, |
| -- we want to do the SU conversion after computing the size in |
| -- this case. |
| |
| if Size.Status = Const then |
| |
| -- If the current value is a multiple of the storage unit, |
| -- then most certainly we can do the conversion now, simply |
| -- by dividing the current value by the storage unit value. |
| -- If this works, we set SU_Convert_Required to False. |
| |
| if Size.Val mod SSU = 0 then |
| |
| Size := |
| (Dynamic, Make_Integer_Literal (Loc, Size.Val / SSU)); |
| SU_Convert_Required := False; |
| |
| -- Otherwise, we go ahead and convert the value in bits, |
| -- and set SU_Convert_Required to True to ensure that the |
| -- final value is indeed properly converted. |
| |
| else |
| Size := (Dynamic, Make_Integer_Literal (Loc, Size.Val)); |
| SU_Convert_Required := True; |
| end if; |
| end if; |
| |
| -- Length is hi-lo+1 |
| |
| Len := Compute_Length (Lo, Hi); |
| |
| -- Check possible range of Len |
| |
| declare |
| OK : Boolean; |
| LLo : Uint; |
| LHi : Uint; |
| |
| begin |
| Set_Parent (Len, E); |
| Determine_Range (Len, OK, LLo, LHi); |
| |
| Len := Convert_To (Standard_Unsigned, Len); |
| |
| -- If we cannot verify that range cannot be super-flat, |
| -- we need a max with zero, since length must be non-neg. |
| |
| if not OK or else LLo < 0 then |
| Len := |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of (Standard_Unsigned, Loc), |
| Attribute_Name => Name_Max, |
| Expressions => New_List ( |
| Make_Integer_Literal (Loc, 0), |
| Len)); |
| end if; |
| end; |
| end if; |
| |
| Next_Index (Indx); |
| end loop; |
| |
| -- Here after processing all bounds to set sizes. If the value is |
| -- a constant, then it is bits, and we just return the value. |
| |
| if Size.Status = Const then |
| return Make_Integer_Literal (Loc, Size.Val); |
| |
| -- Case where the value is dynamic |
| |
| else |
| -- Do convert from bits to SU's if needed |
| |
| if SU_Convert_Required then |
| |
| -- The expression required is (Size.Nod + SU - 1) / SU |
| |
| Size.Nod := |
| Make_Op_Divide (Loc, |
| Left_Opnd => |
| Make_Op_Add (Loc, |
| Left_Opnd => Size.Nod, |
| Right_Opnd => Make_Integer_Literal (Loc, SSU - 1)), |
| Right_Opnd => Make_Integer_Literal (Loc, SSU)); |
| end if; |
| |
| return Size.Nod; |
| end if; |
| end Get_Max_Size; |
| |
| ----------------------- |
| -- Layout_Array_Type -- |
| ----------------------- |
| |
| procedure Layout_Array_Type (E : Entity_Id) is |
| Loc : constant Source_Ptr := Sloc (E); |
| Ctyp : constant Entity_Id := Component_Type (E); |
| Indx : Node_Id; |
| Ityp : Entity_Id; |
| Lo : Node_Id; |
| Hi : Node_Id; |
| S : Uint; |
| Len : Node_Id; |
| |
| Insert_Typ : Entity_Id; |
| -- This is the type with which any generated constants or functions |
| -- will be associated (i.e. inserted into the freeze actions). This |
| -- is normally the type being laid out. The exception occurs when |
| -- we are laying out Itype's which are local to a record type, and |
| -- whose scope is this record type. Such types do not have freeze |
| -- nodes (because we have no place to put them). |
| |
| ------------------------------------ |
| -- How An Array Type is Laid Out -- |
| ------------------------------------ |
| |
| -- Here is what goes on. We need to multiply the component size of |
| -- the array (which has already been set) by the length of each of |
| -- the indexes. If all these values are known at compile time, then |
| -- the resulting size of the array is the appropriate constant value. |
| |
| -- If the component size or at least one bound is dynamic (but no |
| -- discriminants are present), then the size will be computed as an |
| -- expression that calculates the proper size. |
| |
| -- If there is at least one discriminant bound, then the size is also |
| -- computed as an expression, but this expression contains discriminant |
| -- values which are obtained by selecting from a function parameter, and |
| -- the size is given by a function that is passed the variant record in |
| -- question, and whose body is the expression. |
| |
| type Val_Status_Type is (Const, Dynamic, Discrim); |
| |
| type Val_Type (Status : Val_Status_Type := Const) is |
| record |
| case Status is |
| when Const => |
| Val : Uint; |
| -- Calculated value so far if Val_Status = Const |
| |
| when Dynamic | Discrim => |
| Nod : Node_Id; |
| -- Expression value so far if Val_Status /= Const |
| |
| end case; |
| end record; |
| -- Records the value or expression computed so far. Const means that |
| -- the value is constant, and Val is the current constant value. |
| -- Dynamic means that the value is dynamic, and in this case Nod is |
| -- the Node_Id of the expression to compute the value, and Discrim |
| -- means that at least one bound is a discriminant, in which case Nod |
| -- is the expression so far (which will be the body of the function). |
| |
| Size : Val_Type; |
| -- Value of size computed so far. See comments above. |
| |
| Vtyp : Entity_Id := Empty; |
| -- Variant record type for the formal parameter of the |
| -- discriminant function V if Status = Discrim. |
| |
| SU_Convert_Required : Boolean := False; |
| -- This is set to True if the final result must be converted from |
| -- bits to storage units (rounding up to a storage unit boundary). |
| |
| Storage_Divisor : Uint := UI_From_Int (SSU); |
| -- This is the amount that a nonstatic computed size will be divided |
| -- by to convert it from bits to storage units. This is normally |
| -- equal to SSU, but can be reduced in the case of packed components |
| -- that fit evenly into a storage unit. |
| |
| Make_Size_Function : Boolean := False; |
| -- Indicates whether to request that SO_Ref_From_Expr should |
| -- encapsulate the array size expresion in a function. |
| |
| procedure Discrimify (N : in out Node_Id); |
| -- If N represents a discriminant, then the Size.Status is set to |
| -- Discrim, and Vtyp is set. The parameter N is replaced with the |
| -- proper expression to extract the discriminant value from V. |
| |
| ---------------- |
| -- Discrimify -- |
| ---------------- |
| |
| procedure Discrimify (N : in out Node_Id) is |
| Decl : Node_Id; |
| Typ : Entity_Id; |
| |
| begin |
| if Nkind (N) = N_Identifier |
| and then Ekind (Entity (N)) = E_Discriminant |
| then |
| Set_Size_Depends_On_Discriminant (E); |
| |
| if Size.Status /= Discrim then |
| Decl := Parent (Parent (Entity (N))); |
| Size := (Discrim, Size.Nod); |
| Vtyp := Defining_Identifier (Decl); |
| end if; |
| |
| Typ := Etype (N); |
| |
| N := |
| Make_Selected_Component (Loc, |
| Prefix => Make_Identifier (Loc, Chars => Vname), |
| Selector_Name => New_Occurrence_Of (Entity (N), Loc)); |
| |
| -- Set the Etype attributes of the selected name and its prefix. |
| -- Analyze_And_Resolve can't be called here because the Vname |
| -- entity denoted by the prefix will not yet exist (it's created |
| -- by SO_Ref_From_Expr, called at the end of Layout_Array_Type). |
| |
| Set_Etype (Prefix (N), Vtyp); |
| Set_Etype (N, Typ); |
| end if; |
| end Discrimify; |
| |
| -- Start of processing for Layout_Array_Type |
| |
| begin |
| -- Default alignment is component alignment |
| |
| if Unknown_Alignment (E) then |
| Set_Alignment (E, Alignment (Ctyp)); |
| end if; |
| |
| -- Calculate proper type for insertions |
| |
| if Is_Record_Type (Scope (E)) then |
| Insert_Typ := Scope (E); |
| else |
| Insert_Typ := E; |
| end if; |
| |
| -- If the component type is a generic formal type then there's no point |
| -- in determining a size for the array type. |
| |
| if Is_Generic_Type (Ctyp) then |
| return; |
| end if; |
| |
| -- Deal with component size if base type |
| |
| if Ekind (E) = E_Array_Type then |
| |
| -- Cannot do anything if Esize of component type unknown |
| |
| if Unknown_Esize (Ctyp) then |
| return; |
| end if; |
| |
| -- Set component size if not set already |
| |
| if Unknown_Component_Size (E) then |
| Set_Component_Size (E, Esize (Ctyp)); |
| end if; |
| end if; |
| |
| -- (RM 13.3 (48)) says that the size of an unconstrained array |
| -- is implementation defined. We choose to leave it as Unknown |
| -- here, and the actual behavior is determined by the back end. |
| |
| if not Is_Constrained (E) then |
| return; |
| end if; |
| |
| -- Initialize status from component size |
| |
| if Known_Static_Component_Size (E) then |
| Size := (Const, Component_Size (E)); |
| |
| else |
| Size := (Dynamic, Expr_From_SO_Ref (Loc, Component_Size (E))); |
| end if; |
| |
| -- Loop to process array indices |
| |
| Indx := First_Index (E); |
| while Present (Indx) loop |
| Ityp := Etype (Indx); |
| |
| -- If an index of the array is a generic formal type then there's |
| -- no point in determining a size for the array type. |
| |
| if Is_Generic_Type (Ityp) then |
| return; |
| end if; |
| |
| Lo := Type_Low_Bound (Ityp); |
| Hi := Type_High_Bound (Ityp); |
| |
| -- Value of the current subscript range is statically known |
| |
| if Compile_Time_Known_Value (Lo) |
| and then Compile_Time_Known_Value (Hi) |
| then |
| S := Expr_Value (Hi) - Expr_Value (Lo) + 1; |
| |
| -- If known flat bound, entire size of array is zero! |
| |
| if S <= 0 then |
| Set_Esize (E, Uint_0); |
| Set_RM_Size (E, Uint_0); |
| return; |
| end if; |
| |
| -- If constant, evolve value |
| |
| if Size.Status = Const then |
| Size.Val := Size.Val * S; |
| |
| -- Current value is dynamic |
| |
| else |
| -- An interesting little optimization, if we have a pending |
| -- conversion from bits to storage units, and the current |
| -- length is a multiple of the storage unit size, then we |
| -- can take the factor out here statically, avoiding some |
| -- extra dynamic computations at the end. |
| |
| if SU_Convert_Required and then S mod SSU = 0 then |
| S := S / SSU; |
| SU_Convert_Required := False; |
| end if; |
| |
| -- Now go ahead and evolve the expression |
| |
| Size.Nod := |
| Assoc_Multiply (Loc, |
| Left_Opnd => Size.Nod, |
| Right_Opnd => |
| Make_Integer_Literal (Loc, Intval => S)); |
| end if; |
| |
| -- Value of the current subscript range is dynamic |
| |
| else |
| -- If the current size value is constant, then here is where we |
| -- make a transition to dynamic values, which are always stored |
| -- in storage units, However, we do not want to convert to SU's |
| -- too soon, consider the case of a packed array of single bits, |
| -- we want to do the SU conversion after computing the size in |
| -- this case. |
| |
| if Size.Status = Const then |
| |
| -- If the current value is a multiple of the storage unit, |
| -- then most certainly we can do the conversion now, simply |
| -- by dividing the current value by the storage unit value. |
| -- If this works, we set SU_Convert_Required to False. |
| |
| if Size.Val mod SSU = 0 then |
| Size := |
| (Dynamic, Make_Integer_Literal (Loc, Size.Val / SSU)); |
| SU_Convert_Required := False; |
| |
| -- If the current value is a factor of the storage unit, |
| -- then we can use a value of one for the size and reduce |
| -- the strength of the later division. |
| |
| elsif SSU mod Size.Val = 0 then |
| Storage_Divisor := SSU / Size.Val; |
| Size := (Dynamic, Make_Integer_Literal (Loc, Uint_1)); |
| SU_Convert_Required := True; |
| |
| -- Otherwise, we go ahead and convert the value in bits, |
| -- and set SU_Convert_Required to True to ensure that the |
| -- final value is indeed properly converted. |
| |
| else |
| Size := (Dynamic, Make_Integer_Literal (Loc, Size.Val)); |
| SU_Convert_Required := True; |
| end if; |
| end if; |
| |
| Discrimify (Lo); |
| Discrimify (Hi); |
| |
| -- Length is hi-lo+1 |
| |
| Len := Compute_Length (Lo, Hi); |
| |
| -- If Len isn't a Length attribute, then its range needs to |
| -- be checked a possible Max with zero needs to be computed. |
| |
| if Nkind (Len) /= N_Attribute_Reference |
| or else Attribute_Name (Len) /= Name_Length |
| then |
| declare |
| OK : Boolean; |
| LLo : Uint; |
| LHi : Uint; |
| |
| begin |
| -- Check possible range of Len |
| |
| Set_Parent (Len, E); |
| Determine_Range (Len, OK, LLo, LHi); |
| |
| Len := Convert_To (Standard_Unsigned, Len); |
| |
| -- If range definitely flat or superflat, |
| -- result size is zero |
| |
| if OK and then LHi <= 0 then |
| Set_Esize (E, Uint_0); |
| Set_RM_Size (E, Uint_0); |
| return; |
| end if; |
| |
| -- If we cannot verify that range cannot be super-flat, |
| -- we need a maximum with zero, since length cannot be |
| -- negative. |
| |
| if not OK or else LLo < 0 then |
| Len := |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of (Standard_Unsigned, Loc), |
| Attribute_Name => Name_Max, |
| Expressions => New_List ( |
| Make_Integer_Literal (Loc, 0), |
| Len)); |
| end if; |
| end; |
| end if; |
| |
| -- At this stage, Len has the expression for the length |
| |
| Size.Nod := |
| Assoc_Multiply (Loc, |
| Left_Opnd => Size.Nod, |
| Right_Opnd => Len); |
| end if; |
| |
| Next_Index (Indx); |
| end loop; |
| |
| -- Here after processing all bounds to set sizes. If the value is |
| -- a constant, then it is bits, and the only thing we need to do |
| -- is to check against explicit given size and do alignment adjust. |
| |
| if Size.Status = Const then |
| Set_And_Check_Static_Size (E, Size.Val, Size.Val); |
| Adjust_Esize_Alignment (E); |
| |
| -- Case where the value is dynamic |
| |
| else |
| -- Do convert from bits to SU's if needed |
| |
| if SU_Convert_Required then |
| |
| -- The expression required is: |
| -- (Size.Nod + Storage_Divisor - 1) / Storage_Divisor |
| |
| Size.Nod := |
| Make_Op_Divide (Loc, |
| Left_Opnd => |
| Make_Op_Add (Loc, |
| Left_Opnd => Size.Nod, |
| Right_Opnd => Make_Integer_Literal |
| (Loc, Storage_Divisor - 1)), |
| Right_Opnd => Make_Integer_Literal (Loc, Storage_Divisor)); |
| end if; |
| |
| -- If the array entity is not declared at the library level and its |
| -- not nested within a subprogram that is marked for inlining, then |
| -- we request that the size expression be encapsulated in a function. |
| -- Since this expression is not needed in most cases, we prefer not |
| -- to incur the overhead of the computation on calls to the enclosing |
| -- subprogram except for subprograms that require the size. |
| |
| if not Is_Library_Level_Entity (E) then |
| Make_Size_Function := True; |
| |
| declare |
| Parent_Subp : Entity_Id := Enclosing_Subprogram (E); |
| |
| begin |
| while Present (Parent_Subp) loop |
| if Is_Inlined (Parent_Subp) then |
| Make_Size_Function := False; |
| exit; |
| end if; |
| |
| Parent_Subp := Enclosing_Subprogram (Parent_Subp); |
| end loop; |
| end; |
| end if; |
| |
| -- Now set the dynamic size (the Value_Size is always the same |
| -- as the Object_Size for arrays whose length is dynamic). |
| |
| -- ??? If Size.Status = Dynamic, Vtyp will not have been set. |
| -- The added initialization sets it to Empty now, but is this |
| -- correct? |
| |
| Set_Esize |
| (E, |
| SO_Ref_From_Expr |
| (Size.Nod, Insert_Typ, Vtyp, Make_Func => Make_Size_Function)); |
| Set_RM_Size (E, Esize (E)); |
| end if; |
| end Layout_Array_Type; |
| |
| ------------------- |
| -- Layout_Object -- |
| ------------------- |
| |
| procedure Layout_Object (E : Entity_Id) is |
| T : constant Entity_Id := Etype (E); |
| |
| begin |
| -- Nothing to do if backend does layout |
| |
| if not Frontend_Layout_On_Target then |
| return; |
| end if; |
| |
| -- Set size if not set for object and known for type. Use the |
| -- RM_Size if that is known for the type and Esize is not. |
| |
| if Unknown_Esize (E) then |
| if Known_Esize (T) then |
| Set_Esize (E, Esize (T)); |
| |
| elsif Known_RM_Size (T) then |
| Set_Esize (E, RM_Size (T)); |
| end if; |
| end if; |
| |
| -- Set alignment from type if unknown and type alignment known |
| |
| if Unknown_Alignment (E) and then Known_Alignment (T) then |
| Set_Alignment (E, Alignment (T)); |
| end if; |
| |
| -- Make sure size and alignment are consistent |
| |
| Adjust_Esize_Alignment (E); |
| |
| -- Final adjustment, if we don't know the alignment, and the Esize |
| -- was not set by an explicit Object_Size attribute clause, then |
| -- we reset the Esize to unknown, since we really don't know it. |
| |
| if Unknown_Alignment (E) |
| and then not Has_Size_Clause (E) |
| then |
| Set_Esize (E, Uint_0); |
| end if; |
| end Layout_Object; |
| |
| ------------------------ |
| -- Layout_Record_Type -- |
| ------------------------ |
| |
| procedure Layout_Record_Type (E : Entity_Id) is |
| Loc : constant Source_Ptr := Sloc (E); |
| Decl : Node_Id; |
| |
| Comp : Entity_Id; |
| -- Current component being laid out |
| |
| Prev_Comp : Entity_Id; |
| -- Previous laid out component |
| |
| procedure Get_Next_Component_Location |
| (Prev_Comp : Entity_Id; |
| Align : Uint; |
| New_Npos : out SO_Ref; |
| New_Fbit : out SO_Ref; |
| New_NPMax : out SO_Ref; |
| Force_SU : Boolean); |
| -- Given the previous component in Prev_Comp, which is already laid |
| -- out, and the alignment of the following component, lays out the |
| -- following component, and returns its starting position in New_Npos |
| -- (Normalized_Position value), New_Fbit (Normalized_First_Bit value), |
| -- and New_NPMax (Normalized_Position_Max value). If Prev_Comp is empty |
| -- (no previous component is present), then New_Npos, New_Fbit and |
| -- New_NPMax are all set to zero on return. This procedure is also |
| -- used to compute the size of a record or variant by giving it the |
| -- last component, and the record alignment. Force_SU is used to force |
| -- the new component location to be aligned on a storage unit boundary, |
| -- even in a packed record, False means that the new position does not |
| -- need to be bumped to a storage unit boundary, True means a storage |
| -- unit boundary is always required. |
| |
| procedure Layout_Component (Comp : Entity_Id; Prev_Comp : Entity_Id); |
| -- Lays out component Comp, given Prev_Comp, the previously laid-out |
| -- component (Prev_Comp = Empty if no components laid out yet). The |
| -- alignment of the record itself is also updated if needed. Both |
| -- Comp and Prev_Comp can be either components or discriminants. |
| |
| procedure Layout_Components |
| (From : Entity_Id; |
| To : Entity_Id; |
| Esiz : out SO_Ref; |
| RM_Siz : out SO_Ref); |
| -- This procedure lays out the components of the given component list |
| -- which contains the components starting with From and ending with To. |
| -- The Next_Entity chain is used to traverse the components. On entry, |
| -- Prev_Comp is set to the component preceding the list, so that the |
| -- list is laid out after this component. Prev_Comp is set to Empty if |
| -- the component list is to be laid out starting at the start of the |
| -- record. On return, the components are all laid out, and Prev_Comp is |
| -- set to the last laid out component. On return, Esiz is set to the |
| -- resulting Object_Size value, which is the length of the record up |
| -- to and including the last laid out entity. For Esiz, the value is |
| -- adjusted to match the alignment of the record. RM_Siz is similarly |
| -- set to the resulting Value_Size value, which is the same length, but |
| -- not adjusted to meet the alignment. Note that in the case of variant |
| -- records, Esiz represents the maximum size. |
| |
| procedure Layout_Non_Variant_Record; |
| -- Procedure called to lay out a non-variant record type or subtype |
| |
| procedure Layout_Variant_Record; |
| -- Procedure called to lay out a variant record type. Decl is set to the |
| -- full type declaration for the variant record. |
| |
| --------------------------------- |
| -- Get_Next_Component_Location -- |
| --------------------------------- |
| |
| procedure Get_Next_Component_Location |
| (Prev_Comp : Entity_Id; |
| Align : Uint; |
| New_Npos : out SO_Ref; |
| New_Fbit : out SO_Ref; |
| New_NPMax : out SO_Ref; |
| Force_SU : Boolean) |
| is |
| begin |
| -- No previous component, return zero position |
| |
| if No (Prev_Comp) then |
| New_Npos := Uint_0; |
| New_Fbit := Uint_0; |
| New_NPMax := Uint_0; |
| return; |
| end if; |
| |
| -- Here we have a previous component |
| |
| declare |
| Loc : constant Source_Ptr := Sloc (Prev_Comp); |
| |
| Old_Npos : constant SO_Ref := Normalized_Position (Prev_Comp); |
| Old_Fbit : constant SO_Ref := Normalized_First_Bit (Prev_Comp); |
| Old_NPMax : constant SO_Ref := Normalized_Position_Max (Prev_Comp); |
| Old_Esiz : constant SO_Ref := Esize (Prev_Comp); |
| |
| Old_Maxsz : Node_Id; |
| -- Expression representing maximum size of previous component |
| |
| begin |
| -- Case where previous field had a dynamic size |
| |
| if Is_Dynamic_SO_Ref (Esize (Prev_Comp)) then |
| |
| -- If the previous field had a dynamic length, then it is |
| -- required to occupy an integral number of storage units, |
| -- and start on a storage unit boundary. This means that |
| -- the Normalized_First_Bit value is zero in the previous |
| -- component, and the new value is also set to zero. |
| |
| New_Fbit := Uint_0; |
| |
| -- In this case, the new position is given by an expression |
| -- that is the sum of old normalized position and old size. |
| |
| New_Npos := |
| SO_Ref_From_Expr |
| (Assoc_Add (Loc, |
| Left_Opnd => |
| Expr_From_SO_Ref (Loc, Old_Npos), |
| Right_Opnd => |
| Expr_From_SO_Ref (Loc, Old_Esiz, Prev_Comp)), |
| Ins_Type => E, |
| Vtype => E); |
| |
| -- Get maximum size of previous component |
| |
| if Size_Depends_On_Discriminant (Etype (Prev_Comp)) then |
| Old_Maxsz := Get_Max_Size (Etype (Prev_Comp)); |
| else |
| Old_Maxsz := Expr_From_SO_Ref (Loc, Old_Esiz, Prev_Comp); |
| end if; |
| |
| -- Now we can compute the new max position. If the max size |
| -- is static and the old position is static, then we can |
| -- compute the new position statically. |
| |
| if Nkind (Old_Maxsz) = N_Integer_Literal |
| and then Known_Static_Normalized_Position_Max (Prev_Comp) |
| then |
| New_NPMax := Old_NPMax + Intval (Old_Maxsz); |
| |
| -- Otherwise new max position is dynamic |
| |
| else |
| New_NPMax := |
| SO_Ref_From_Expr |
| (Assoc_Add (Loc, |
| Left_Opnd => Expr_From_SO_Ref (Loc, Old_NPMax), |
| Right_Opnd => Old_Maxsz), |
| Ins_Type => E, |
| Vtype => E); |
| end if; |
| |
| -- Previous field has known static Esize |
| |
| else |
| New_Fbit := Old_Fbit + Old_Esiz; |
| |
| -- Bump New_Fbit to storage unit boundary if required |
| |
| if New_Fbit /= 0 and then Force_SU then |
| New_Fbit := (New_Fbit + SSU - 1) / SSU * SSU; |
| end if; |
| |
| -- If old normalized position is static, we can go ahead |
| -- and compute the new normalized position directly. |
| |
| if Known_Static_Normalized_Position (Prev_Comp) then |
| New_Npos := Old_Npos; |
| |
| if New_Fbit >= SSU then |
| New_Npos := New_Npos + New_Fbit / SSU; |
| New_Fbit := New_Fbit mod SSU; |
| end if; |
| |
| -- Bump alignment if stricter than prev |
| |
| if Align > Alignment (Etype (Prev_Comp)) then |
| New_Npos := (New_Npos + Align - 1) / Align * Align; |
| end if; |
| |
| -- The max position is always equal to the position if |
| -- the latter is static, since arrays depending on the |
| -- values of discriminants never have static sizes. |
| |
| New_NPMax := New_Npos; |
| return; |
| |
| -- Case of old normalized position is dynamic |
| |
| else |
| -- If new bit position is within the current storage unit, |
| -- we can just copy the old position as the result position |
| -- (we have already set the new first bit value). |
| |
| if New_Fbit < SSU then |
| New_Npos := Old_Npos; |
| New_NPMax := Old_NPMax; |
| |
| -- If new bit position is past the current storage unit, we |
| -- need to generate a new dynamic value for the position |
| -- ??? need to deal with alignment |
| |
| else |
| New_Npos := |
| SO_Ref_From_Expr |
| (Assoc_Add (Loc, |
| Left_Opnd => Expr_From_SO_Ref (Loc, Old_Npos), |
| Right_Opnd => |
| Make_Integer_Literal (Loc, |
| Intval => New_Fbit / SSU)), |
| Ins_Type => E, |
| Vtype => E); |
| |
| New_NPMax := |
| SO_Ref_From_Expr |
| (Assoc_Add (Loc, |
| Left_Opnd => Expr_From_SO_Ref (Loc, Old_NPMax), |
| Right_Opnd => |
| Make_Integer_Literal (Loc, |
| Intval => New_Fbit / SSU)), |
| Ins_Type => E, |
| Vtype => E); |
| New_Fbit := New_Fbit mod SSU; |
| end if; |
| end if; |
| end if; |
| end; |
| end Get_Next_Component_Location; |
| |
| ---------------------- |
| -- Layout_Component -- |
| ---------------------- |
| |
| procedure Layout_Component (Comp : Entity_Id; Prev_Comp : Entity_Id) is |
| Ctyp : constant Entity_Id := Etype (Comp); |
| Npos : SO_Ref; |
| Fbit : SO_Ref; |
| NPMax : SO_Ref; |
| Forc : Boolean; |
| |
| begin |
| -- Parent field is always at start of record, this will overlap |
| -- the actual fields that are part of the parent, and that's fine |
| |
| if Chars (Comp) = Name_uParent then |
| Set_Normalized_Position (Comp, Uint_0); |
| Set_Normalized_First_Bit (Comp, Uint_0); |
| Set_Normalized_Position_Max (Comp, Uint_0); |
| Set_Component_Bit_Offset (Comp, Uint_0); |
| Set_Esize (Comp, Esize (Ctyp)); |
| return; |
| end if; |
| |
| -- Check case of type of component has a scope of the record we |
| -- are laying out. When this happens, the type in question is an |
| -- Itype that has not yet been laid out (that's because such |
| -- types do not get frozen in the normal manner, because there |
| -- is no place for the freeze nodes). |
| |
| if Scope (Ctyp) = E then |
| Layout_Type (Ctyp); |
| end if; |
| |
| -- Increase alignment of record if necessary. Note that we do not |
| -- do this for packed records, which have an alignment of one by |
| -- default, or for records for which an explicit alignment was |
| -- specified with an alignment clause. |
| |
| if not Is_Packed (E) |
| and then not Has_Alignment_Clause (E) |
| and then Alignment (Ctyp) > Alignment (E) |
| then |
| Set_Alignment (E, Alignment (Ctyp)); |
| end if; |
| |
| -- If component already laid out, then we are done |
| |
| if Known_Normalized_Position (Comp) then |
| return; |
| end if; |
| |
| -- Set size of component from type. We use the Esize except in a |
| -- packed record, where we use the RM_Size (since that is exactly |
| -- what the RM_Size value, as distinct from the Object_Size is |
| -- useful for!) |
| |
| if Is_Packed (E) then |
| Set_Esize (Comp, RM_Size (Ctyp)); |
| else |
| Set_Esize (Comp, Esize (Ctyp)); |
| end if; |
| |
| -- Compute the component position from the previous one. See if |
| -- current component requires being on a storage unit boundary. |
| |
| -- If record is not packed, we always go to a storage unit boundary |
| |
| if not Is_Packed (E) then |
| Forc := True; |
| |
| -- Packed cases |
| |
| else |
| -- Elementary types do not need SU boundary in packed record |
| |
| if Is_Elementary_Type (Ctyp) then |
| Forc := False; |
| |
| -- Packed array types with a modular packed array type do not |
| -- force a storage unit boundary (since the code generation |
| -- treats these as equivalent to the underlying modular type), |
| |
| elsif Is_Array_Type (Ctyp) |
| and then Is_Bit_Packed_Array (Ctyp) |
| and then Is_Modular_Integer_Type (Packed_Array_Type (Ctyp)) |
| then |
| Forc := False; |
| |
| -- Record types with known length less than or equal to the length |
| -- of long long integer can also be unaligned, since they can be |
| -- treated as scalars. |
| |
| elsif Is_Record_Type (Ctyp) |
| and then not Is_Dynamic_SO_Ref (Esize (Ctyp)) |
| and then Esize (Ctyp) <= Esize (Standard_Long_Long_Integer) |
| then |
| Forc := False; |
| |
| -- All other cases force a storage unit boundary, even when packed |
| |
| else |
| Forc := True; |
| end if; |
| end if; |
| |
| -- Now get the next component location |
| |
| Get_Next_Component_Location |
| (Prev_Comp, Alignment (Ctyp), Npos, Fbit, NPMax, Forc); |
| Set_Normalized_Position (Comp, Npos); |
| Set_Normalized_First_Bit (Comp, Fbit); |
| Set_Normalized_Position_Max (Comp, NPMax); |
| |
| -- Set Component_Bit_Offset in the static case |
| |
| if Known_Static_Normalized_Position (Comp) |
| and then Known_Normalized_First_Bit (Comp) |
| then |
| Set_Component_Bit_Offset (Comp, SSU * Npos + Fbit); |
| end if; |
| end Layout_Component; |
| |
| ----------------------- |
| -- Layout_Components -- |
| ----------------------- |
| |
| procedure Layout_Components |
| (From : Entity_Id; |
| To : Entity_Id; |
| Esiz : out SO_Ref; |
| RM_Siz : out SO_Ref) |
| is |
| End_Npos : SO_Ref; |
| End_Fbit : SO_Ref; |
| End_NPMax : SO_Ref; |
| |
| begin |
| -- Only lay out components if there are some to lay out! |
| |
| if Present (From) then |
| |
| -- Lay out components with no component clauses |
| |
| Comp := From; |
| loop |
| if Ekind (Comp) = E_Component |
| or else Ekind (Comp) = E_Discriminant |
| then |
| -- The compatibility of component clauses with composite |
| -- types isn't checked in Sem_Ch13, so we check it here. |
| |
| if Present (Component_Clause (Comp)) then |
| if Is_Composite_Type (Etype (Comp)) |
| and then Esize (Comp) < RM_Size (Etype (Comp)) |
| then |
| Error_Msg_Uint_1 := RM_Size (Etype (Comp)); |
| Error_Msg_NE |
| ("size for & too small, minimum allowed is ^", |
| Component_Clause (Comp), |
| Comp); |
| end if; |
| |
| else |
| Layout_Component (Comp, Prev_Comp); |
| Prev_Comp := Comp; |
| end if; |
| end if; |
| |
| exit when Comp = To; |
| Next_Entity (Comp); |
| end loop; |
| end if; |
| |
| -- Set size fields, both are zero if no components |
| |
| if No (Prev_Comp) then |
| Esiz := Uint_0; |
| RM_Siz := Uint_0; |
| |
| else |
| -- First the object size, for which we align past the last |
| -- field to the alignment of the record (the object size |
| -- is required to be a multiple of the alignment). |
| |
| Get_Next_Component_Location |
| (Prev_Comp, |
| Alignment (E), |
| End_Npos, |
| End_Fbit, |
| End_NPMax, |
| Force_SU => True); |
| |
| -- If the resulting normalized position is a dynamic reference, |
| -- then the size is dynamic, and is stored in storage units. |
| -- In this case, we set the RM_Size to the same value, it is |
| -- simply not worth distinguishing Esize and RM_Size values in |
| -- the dynamic case, since the RM has nothing to say about them. |
| |
| -- Note that a size cannot have been given in this case, since |
| -- size specifications cannot be given for variable length types. |
| |
| declare |
| Align : constant Uint := Alignment (E); |
| |
| begin |
| if Is_Dynamic_SO_Ref (End_Npos) then |
| RM_Siz := End_Npos; |
| |
| -- Set the Object_Size allowing for alignment. In the |
| -- dynamic case, we have to actually do the runtime |
| -- computation. We can skip this in the non-packed |
| -- record case if the last component has a smaller |
| -- alignment than the overall record alignment. |
| |
| if Is_Dynamic_SO_Ref (End_NPMax) then |
| Esiz := End_NPMax; |
| |
| if Is_Packed (E) |
| or else Alignment (Etype (Prev_Comp)) < Align |
| then |
| -- The expression we build is |
| -- (expr + align - 1) / align * align |
| |
| Esiz := |
| SO_Ref_From_Expr |
| (Expr => |
| Make_Op_Multiply (Loc, |
| Left_Opnd => |
| Make_Op_Divide (Loc, |
| Left_Opnd => |
| Make_Op_Add (Loc, |
| Left_Opnd => |
| Expr_From_SO_Ref (Loc, Esiz), |
| Right_Opnd => |
| Make_Integer_Literal (Loc, |
| Intval => Align - 1)), |
| Right_Opnd => |
| Make_Integer_Literal (Loc, Align)), |
| Right_Opnd => |
| Make_Integer_Literal (Loc, Align)), |
| Ins_Type => E, |
| Vtype => E); |
| end if; |
| |
| -- Here Esiz is static, so we can adjust the alignment |
| -- directly go give the required aligned value. |
| |
| else |
| Esiz := (End_NPMax + Align - 1) / Align * Align * SSU; |
| end if; |
| |
| -- Case where computed size is static |
| |
| else |
| -- The ending size was computed in Npos in storage units, |
| -- but the actual size is stored in bits, so adjust |
| -- accordingly. We also adjust the size to match the |
| -- alignment here. |
| |
| Esiz := (End_NPMax + Align - 1) / Align * Align * SSU; |
| |
| -- Compute the resulting Value_Size (RM_Size). For this |
| -- purpose we do not force alignment of the record or |
| -- storage size alignment of the result. |
| |
| Get_Next_Component_Location |
| (Prev_Comp, |
| Uint_0, |
| End_Npos, |
| End_Fbit, |
| End_NPMax, |
| Force_SU => False); |
| |
| RM_Siz := End_Npos * SSU + End_Fbit; |
| Set_And_Check_Static_Size (E, Esiz, RM_Siz); |
| end if; |
| end; |
| end if; |
| end Layout_Components; |
| |
| ------------------------------- |
| -- Layout_Non_Variant_Record -- |
| ------------------------------- |
| |
| procedure Layout_Non_Variant_Record is |
| Esiz : SO_Ref; |
| RM_Siz : SO_Ref; |
| |
| begin |
| Layout_Components (First_Entity (E), Last_Entity (E), Esiz, RM_Siz); |
| Set_Esize (E, Esiz); |
| Set_RM_Size (E, RM_Siz); |
| end Layout_Non_Variant_Record; |
| |
| --------------------------- |
| -- Layout_Variant_Record -- |
| --------------------------- |
| |
| procedure Layout_Variant_Record is |
| Tdef : constant Node_Id := Type_Definition (Decl); |
| Dlist : constant List_Id := Discriminant_Specifications (Decl); |
| Esiz : SO_Ref; |
| RM_Siz : SO_Ref; |
| |
| RM_Siz_Expr : Node_Id := Empty; |
| -- Expression for the evolving RM_Siz value. This is typically a |
| -- conditional expression which involves tests of discriminant |
| -- values that are formed as references to the entity V. At |
| -- the end of scanning all the components, a suitable function |
| -- is constructed in which V is the parameter. |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| procedure Layout_Component_List |
| (Clist : Node_Id; |
| Esiz : out SO_Ref; |
| RM_Siz_Expr : out Node_Id); |
| -- Recursive procedure, called to lay out one component list |
| -- Esiz and RM_Siz_Expr are set to the Object_Size and Value_Size |
| -- values respectively representing the record size up to and |
| -- including the last component in the component list (including |
| -- any variants in this component list). RM_Siz_Expr is returned |
| -- as an expression which may in the general case involve some |
| -- references to the discriminants of the current record value, |
| -- referenced by selecting from the entity V. |
| |
| --------------------------- |
| -- Layout_Component_List -- |
| --------------------------- |
| |
| procedure Layout_Component_List |
| (Clist : Node_Id; |
| Esiz : out SO_Ref; |
| RM_Siz_Expr : out Node_Id) |
| is |
| Citems : constant List_Id := Component_Items (Clist); |
| Vpart : constant Node_Id := Variant_Part (Clist); |
| Prv : Node_Id; |
| Var : Node_Id; |
| RM_Siz : Uint; |
| RMS_Ent : Entity_Id; |
| |
| begin |
| if Is_Non_Empty_List (Citems) then |
| Layout_Components |
| (From => Defining_Identifier (First (Citems)), |
| To => Defining_Identifier (Last (Citems)), |
| Esiz => Esiz, |
| RM_Siz => RM_Siz); |
| else |
| Layout_Components (Empty, Empty, Esiz, RM_Siz); |
| end if; |
| |
| -- Case where no variants are present in the component list |
| |
| if No (Vpart) then |
| |
| -- The Esiz value has been correctly set by the call to |
| -- Layout_Components, so there is nothing more to be done. |
| |
| -- For RM_Siz, we have an SO_Ref value, which we must convert |
| -- to an appropriate expression. |
| |
| if Is_Static_SO_Ref (RM_Siz) then |
| RM_Siz_Expr := |
| Make_Integer_Literal (Loc, |
| Intval => RM_Siz); |
| |
| else |
| RMS_Ent := Get_Dynamic_SO_Entity (RM_Siz); |
| |
| -- If the size is represented by a function, then we |
| -- create an appropriate function call using V as |
| -- the parameter to the call. |
| |
| if Is_Discrim_SO_Function (RMS_Ent) then |
| RM_Siz_Expr := |
| Make_Function_Call (Loc, |
| Name => New_Occurrence_Of (RMS_Ent, Loc), |
| Parameter_Associations => New_List ( |
| Make_Identifier (Loc, Chars => Vname))); |
| |
| -- If the size is represented by a constant, then the |
| -- expression we want is a reference to this constant |
| |
| else |
| RM_Siz_Expr := New_Occurrence_Of (RMS_Ent, Loc); |
| end if; |
| end if; |
| |
| -- Case where variants are present in this component list |
| |
| else |
| declare |
| EsizV : SO_Ref; |
| RM_SizV : Node_Id; |
| Dchoice : Node_Id; |
| Discrim : Node_Id; |
| Dtest : Node_Id; |
| |
| begin |
| RM_Siz_Expr := Empty; |
| Prv := Prev_Comp; |
| |
| Var := Last (Variants (Vpart)); |
| while Present (Var) loop |
| Prev_Comp := Prv; |
| Layout_Component_List |
| (Component_List (Var), EsizV, RM_SizV); |
| |
| -- Set the Object_Size. If this is the first variant, |
| -- we just set the size of this first variant. |
| |
| if Var = Last (Variants (Vpart)) then |
| Esiz := EsizV; |
| |
| -- Otherwise the Object_Size is formed as a maximum |
| -- of Esiz so far from previous variants, and the new |
| -- Esiz value from the variant we just processed. |
| |
| -- If both values are static, we can just compute the |
| -- maximum directly to save building junk nodes. |
| |
| elsif not Is_Dynamic_SO_Ref (Esiz) |
| and then not Is_Dynamic_SO_Ref (EsizV) |
| then |
| Esiz := UI_Max (Esiz, EsizV); |
| |
| -- If either value is dynamic, then we have to generate |
| -- an appropriate Standard_Unsigned'Max attribute call. |
| |
| else |
| Esiz := |
| SO_Ref_From_Expr |
| (Make_Attribute_Reference (Loc, |
| Attribute_Name => Name_Max, |
| Prefix => |
| New_Occurrence_Of (Standard_Unsigned, Loc), |
| Expressions => New_List ( |
| Expr_From_SO_Ref (Loc, Esiz), |
| Expr_From_SO_Ref (Loc, EsizV))), |
| Ins_Type => E, |
| Vtype => E); |
| end if; |
| |
| -- Now deal with Value_Size (RM_Siz). We are aiming at |
| -- an expression that looks like: |
| |
| -- if xxDx (V.disc) then rmsiz1 |
| -- else if xxDx (V.disc) then rmsiz2 |
| -- else ... |
| |
| -- Where rmsiz1, rmsiz2... are the RM_Siz values for the |
| -- individual variants, and xxDx are the discriminant |
| -- checking functions generated for the variant type. |
| |
| -- If this is the first variant, we simply set the |
| -- result as the expression. Note that this takes |
| -- care of the others case. |
| |
| if No (RM_Siz_Expr) then |
| RM_Siz_Expr := Bits_To_SU (RM_SizV); |
| |
| -- Otherwise construct the appropriate test |
| |
| else |
| -- Discriminant to be tested |
| |
| Discrim := |
| Make_Selected_Component (Loc, |
| Prefix => |
| Make_Identifier (Loc, Chars => Vname), |
| Selector_Name => |
| New_Occurrence_Of |
| (Entity (Name (Vpart)), Loc)); |
| |
| -- The test to be used in general is a call to the |
| -- discriminant checking function. However, it is |
| -- definitely worth special casing the very common |
| -- case where a single value is involved. |
| |
| Dchoice := First (Discrete_Choices (Var)); |
| |
| if No (Next (Dchoice)) |
| and then Nkind (Dchoice) /= N_Range |
| then |
| Dtest := |
| Make_Op_Eq (Loc, |
| Left_Opnd => Discrim, |
| Right_Opnd => New_Copy (Dchoice)); |
| |
| -- Generate a call to the discriminant-checking |
| -- function for the variant. Note that the result |
| -- has to be complemented since the function returns |
| -- False when the passed discriminant value matches. |
| |
| else |
| Dtest := |
| Make_Op_Not (Loc, |
| Right_Opnd => |
| Make_Function_Call (Loc, |
| Name => |
| New_Occurrence_Of |
| (Dcheck_Function (Var), Loc), |
| Parameter_Associations => |
| New_List (Discrim))); |
| end if; |
| |
| RM_Siz_Expr := |
| Make_Conditional_Expression (Loc, |
| Expressions => |
| New_List |
| (Dtest, Bits_To_SU (RM_SizV), RM_Siz_Expr)); |
| end if; |
| |
| Prev (Var); |
| end loop; |
| end; |
| end if; |
| end Layout_Component_List; |
| |
| -- Start of processing for Layout_Variant_Record |
| |
| begin |
| -- We need the discriminant checking functions, since we generate |
| -- calls to these functions for the RM_Size expression, so make |
| -- sure that these functions have been constructed in time. |
| |
| Build_Discr_Checking_Funcs (Decl); |
| |
| -- Lay out the discriminants |
| |
| Layout_Components |
| (From => Defining_Identifier (First (Dlist)), |
| To => Defining_Identifier (Last (Dlist)), |
| Esiz => Esiz, |
| RM_Siz => RM_Siz); |
| |
| -- Lay out the main component list (this will make recursive calls |
| -- to lay out all component lists nested within variants). |
| |
| Layout_Component_List (Component_List (Tdef), Esiz, RM_Siz_Expr); |
| Set_Esize (E, Esiz); |
| |
| -- If the RM_Size is a literal, set its value |
| |
| if Nkind (RM_Siz_Expr) = N_Integer_Literal then |
| Set_RM_Size (E, Intval (RM_Siz_Expr)); |
| |
| -- Otherwise we construct a dynamic SO_Ref |
| |
| else |
| Set_RM_Size (E, |
| SO_Ref_From_Expr |
| (RM_Siz_Expr, |
| Ins_Type => E, |
| Vtype => E)); |
| end if; |
| end Layout_Variant_Record; |
| |
| -- Start of processing for Layout_Record_Type |
| |
| begin |
| -- If this is a cloned subtype, just copy the size fields from the |
| -- original, nothing else needs to be done in this case, since the |
| -- components themselves are all shared. |
| |
| if (Ekind (E) = E_Record_Subtype |
| or else Ekind (E) = E_Class_Wide_Subtype) |
| and then Present (Cloned_Subtype (E)) |
| then |
| Set_Esize (E, Esize (Cloned_Subtype (E))); |
| Set_RM_Size (E, RM_Size (Cloned_Subtype (E))); |
| Set_Alignment (E, Alignment (Cloned_Subtype (E))); |
| |
| -- Another special case, class-wide types. The RM says that the size |
| -- of such types is implementation defined (RM 13.3(48)). What we do |
| -- here is to leave the fields set as unknown values, and the backend |
| -- determines the actual behavior. |
| |
| elsif Ekind (E) = E_Class_Wide_Type then |
| null; |
| |
| -- All other cases |
| |
| else |
| -- Initialize alignment conservatively to 1. This value will |
| -- be increased as necessary during processing of the record. |
| |
| if Unknown_Alignment (E) then |
| Set_Alignment (E, Uint_1); |
| end if; |
| |
| -- Initialize previous component. This is Empty unless there |
| -- are components which have already been laid out by component |
| -- clauses. If there are such components, we start our lay out of |
| -- the remaining components following the last such component. |
| |
| Prev_Comp := Empty; |
| |
| Comp := First_Entity (E); |
| while Present (Comp) loop |
| if (Ekind (Comp) = E_Component |
| or else Ekind (Comp) = E_Discriminant) |
| and then Present (Component_Clause (Comp)) |
| then |
| if No (Prev_Comp) |
| or else |
| Component_Bit_Offset (Comp) > |
| Component_Bit_Offset (Prev_Comp) |
| then |
| Prev_Comp := Comp; |
| end if; |
| end if; |
| |
| Next_Entity (Comp); |
| end loop; |
| |
| -- We have two separate circuits, one for non-variant records and |
| -- one for variant records. For non-variant records, we simply go |
| -- through the list of components. This handles all the non-variant |
| -- cases including those cases of subtypes where there is no full |
| -- type declaration, so the tree cannot be used to drive the layout. |
| -- For variant records, we have to drive the layout from the tree |
| -- since we need to understand the variant structure in this case. |
| |
| if Present (Full_View (E)) then |
| Decl := Declaration_Node (Full_View (E)); |
| else |
| Decl := Declaration_Node (E); |
| end if; |
| |
| -- Scan all the components |
| |
| if Nkind (Decl) = N_Full_Type_Declaration |
| and then Has_Discriminants (E) |
| and then Nkind (Type_Definition (Decl)) = N_Record_Definition |
| and then Present (Component_List (Type_Definition (Decl))) |
| and then |
| Present (Variant_Part (Component_List (Type_Definition (Decl)))) |
| then |
| Layout_Variant_Record; |
| else |
| Layout_Non_Variant_Record; |
| end if; |
| end if; |
| end Layout_Record_Type; |
| |
| ----------------- |
| -- Layout_Type -- |
| ----------------- |
| |
| procedure Layout_Type (E : Entity_Id) is |
| begin |
| -- For string literal types, for now, kill the size always, this |
| -- is because gigi does not like or need the size to be set ??? |
| |
| if Ekind (E) = E_String_Literal_Subtype then |
| Set_Esize (E, Uint_0); |
| Set_RM_Size (E, Uint_0); |
| return; |
| end if; |
| |
| -- For access types, set size/alignment. This is system address |
| -- size, except for fat pointers (unconstrained array access types), |
| -- where the size is two times the address size, to accommodate the |
| -- two pointers that are required for a fat pointer (data and |
| -- template). Note that E_Access_Protected_Subprogram_Type is not |
| -- an access type for this purpose since it is not a pointer but is |
| -- equivalent to a record. For access subtypes, copy the size from |
| -- the base type since Gigi represents them the same way. |
| |
| if Is_Access_Type (E) then |
| |
| -- If Esize already set (e.g. by a size clause), then nothing |
| -- further to be done here. |
| |
| if Known_Esize (E) then |
| null; |
| |
| -- Access to subprogram is a strange beast, and we let the |
| -- backend figure out what is needed (it may be some kind |
| -- of fat pointer, including the static link for example. |
| |
| elsif Ekind (E) = E_Access_Protected_Subprogram_Type then |
| null; |
| |
| -- For access subtypes, copy the size information from base type |
| |
| elsif Ekind (E) = E_Access_Subtype then |
| Set_Size_Info (E, Base_Type (E)); |
| Set_RM_Size (E, RM_Size (Base_Type (E))); |
| |
| -- For other access types, we use either address size, or, if |
| -- a fat pointer is used (pointer-to-unconstrained array case), |
| -- twice the address size to accommodate a fat pointer. |
| |
| else |
| declare |
| Desig : Entity_Id := Designated_Type (E); |
| |
| begin |
| if Is_Private_Type (Desig) |
| and then Present (Full_View (Desig)) |
| then |
| Desig := Full_View (Desig); |
| end if; |
| |
| if Is_Array_Type (Desig) |
| and then not Is_Constrained (Desig) |
| and then not Has_Completion_In_Body (Desig) |
| and then not Debug_Flag_6 |
| then |
| Init_Size (E, 2 * System_Address_Size); |
| |
| -- Check for bad convention set |
| |
| if Warn_On_Export_Import |
| and then |
| (Convention (E) = Convention_C |
| or else |
| Convention (E) = Convention_CPP) |
| then |
| Error_Msg_N |
| ("?this access type does not " & |
| "correspond to C pointer", E); |
| end if; |
| |
| else |
| Init_Size (E, System_Address_Size); |
| end if; |
| end; |
| end if; |
| |
| Set_Prim_Alignment (E); |
| |
| -- Scalar types: set size and alignment |
| |
| elsif Is_Scalar_Type (E) then |
| |
| -- For discrete types, the RM_Size and Esize must be set |
| -- already, since this is part of the earlier processing |
| -- and the front end is always required to lay out the |
| -- sizes of such types (since they are available as static |
| -- attributes). All we do is to check that this rule is |
| -- indeed obeyed! |
| |
| if Is_Discrete_Type (E) then |
| |
| -- If the RM_Size is not set, then here is where we set it. |
| |
| -- Note: an RM_Size of zero looks like not set here, but this |
| -- is a rare case, and we can simply reset it without any harm. |
| |
| if not Known_RM_Size (E) then |
| Set_Discrete_RM_Size (E); |
| end if; |
| |
| -- If Esize for a discrete type is not set then set it |
| |
| if not Known_Esize (E) then |
| declare |
| S : Int := 8; |
| |
| begin |
| loop |
| -- If size is big enough, set it and exit |
| |
| if S >= RM_Size (E) then |
| Init_Esize (E, S); |
| exit; |
| |
| -- If the RM_Size is greater than 64 (happens only |
| -- when strange values are specified by the user, |
| -- then Esize is simply a copy of RM_Size, it will |
| -- be further refined later on) |
| |
| elsif S = 64 then |
| Set_Esize (E, RM_Size (E)); |
| exit; |
| |
| -- Otherwise double possible size and keep trying |
| |
| else |
| S := S * 2; |
| end if; |
| end loop; |
| end; |
| end if; |
| |
| -- For non-discrete sclar types, if the RM_Size is not set, |
| -- then set it now to a copy of the Esize if the Esize is set. |
| |
| else |
| if Known_Esize (E) and then Unknown_RM_Size (E) then |
| Set_RM_Size (E, Esize (E)); |
| end if; |
| end if; |
| |
| Set_Prim_Alignment (E); |
| |
| -- Non-primitive types |
| |
| else |
| -- If RM_Size is known, set Esize if not known |
| |
| if Known_RM_Size (E) and then Unknown_Esize (E) then |
| |
| -- If the alignment is known, we bump the Esize up to the |
| -- next alignment boundary if it is not already on one. |
| |
| if Known_Alignment (E) then |
| declare |
| A : constant Uint := Alignment_In_Bits (E); |
| S : constant SO_Ref := RM_Size (E); |
| |
| begin |
| Set_Esize (E, (S * A + A - 1) / A); |
| end; |
| end if; |
| |
| -- If Esize is set, and RM_Size is not, RM_Size is copied from |
| -- Esize at least for now this seems reasonable, and is in any |
| -- case needed for compatibility with old versions of gigi. |
| -- look to be unknown. |
| |
| elsif Known_Esize (E) and then Unknown_RM_Size (E) then |
| Set_RM_Size (E, Esize (E)); |
| end if; |
| |
| -- For array base types, set component size if object size of |
| -- the component type is known and is a small power of 2 (8, |
| -- 16, 32, 64), since this is what will always be used. |
| |
| if Ekind (E) = E_Array_Type |
| and then Unknown_Component_Size (E) |
| then |
| declare |
| CT : constant Entity_Id := Component_Type (E); |
| |
| begin |
| -- For some reasons, access types can cause trouble, |
| -- So let's just do this for discrete types ??? |
| |
| if Present (CT) |
| and then Is_Discrete_Type (CT) |
| and then Known_Static_Esize (CT) |
| then |
| declare |
| S : constant Uint := Esize (CT); |
| |
| begin |
| if S = 8 or else |
| S = 16 or else |
| S = 32 or else |
| S = 64 |
| then |
| Set_Component_Size (E, Esize (CT)); |
| end if; |
| end; |
| end if; |
| end; |
| end if; |
| end if; |
| |
| -- Lay out array and record types if front end layout set |
| |
| if Frontend_Layout_On_Target then |
| if Is_Array_Type (E) and then not Is_Bit_Packed_Array (E) then |
| Layout_Array_Type (E); |
| elsif Is_Record_Type (E) then |
| Layout_Record_Type (E); |
| end if; |
| |
| -- Case of backend layout, we still do a little in the front end |
| |
| else |
| -- Processing for record types |
| |
| if Is_Record_Type (E) then |
| |
| -- Special remaining processing for record types with a known |
| -- size of 16, 32, or 64 bits whose alignment is not yet set. |
| -- For these types, we set a corresponding alignment matching |
| -- the size if possible, or as large as possible if not. |
| |
| if Convention (E) = Convention_Ada |
| and then not Debug_Flag_Q |
| then |
| Set_Composite_Alignment (E); |
| end if; |
| |
| -- Procressing for array types |
| |
| elsif Is_Array_Type (E) then |
| |
| -- For arrays that are required to be atomic, we do the same |
| -- processing as described above for short records, since we |
| -- really need to have the alignment set for the whole array. |
| |
| if Is_Atomic (E) and then not Debug_Flag_Q then |
| Set_Composite_Alignment (E); |
| end if; |
| |
| -- For unpacked array types, set an alignment of 1 if we know |
| -- that the component alignment is not greater than 1. The reason |
| -- we do this is to avoid unnecessary copying of slices of such |
| -- arrays when passed to subprogram parameters (see special test |
| -- in Exp_Ch6.Expand_Actuals). |
| |
| if not Is_Packed (E) |
| and then Unknown_Alignment (E) |
| then |
| if Known_Static_Component_Size (E) |
| and then Component_Size (E) = 1 |
| then |
| Set_Alignment (E, Uint_1); |
| end if; |
| end if; |
| end if; |
| end if; |
| |
| -- Final step is to check that Esize and RM_Size are compatible |
| |
| if Known_Static_Esize (E) and then Known_Static_RM_Size (E) then |
| if Esize (E) < RM_Size (E) then |
| |
| -- Esize is less than RM_Size. That's not good. First we test |
| -- whether this was set deliberately with an Object_Size clause |
| -- and if so, object to the clause. |
| |
| if Has_Object_Size_Clause (E) then |
| Error_Msg_Uint_1 := RM_Size (E); |
| Error_Msg_F |
| ("object size is too small, minimum is ^", |
| Expression (Get_Attribute_Definition_Clause |
| (E, Attribute_Object_Size))); |
| end if; |
| |
| -- Adjust Esize up to RM_Size value |
| |
| declare |
| Size : constant Uint := RM_Size (E); |
| |
| begin |
| Set_Esize (E, RM_Size (E)); |
| |
| -- For scalar types, increase Object_Size to power of 2, |
| -- but not less than a storage unit in any case (i.e., |
| -- normally this means it will be byte addressable). |
| |
| if Is_Scalar_Type (E) then |
| if Size <= System_Storage_Unit then |
| Init_Esize (E, System_Storage_Unit); |
| elsif Size <= 16 then |
| Init_Esize (E, 16); |
| elsif Size <= 32 then |
| Init_Esize (E, 32); |
| else |
| Set_Esize (E, (Size + 63) / 64 * 64); |
| end if; |
| |
| -- Finally, make sure that alignment is consistent with |
| -- the newly assigned size. |
| |
| while Alignment (E) * System_Storage_Unit < Esize (E) |
| and then Alignment (E) < Maximum_Alignment |
| loop |
| Set_Alignment (E, 2 * Alignment (E)); |
| end loop; |
| end if; |
| end; |
| end if; |
| end if; |
| end Layout_Type; |
| |
| --------------------- |
| -- Rewrite_Integer -- |
| --------------------- |
| |
| procedure Rewrite_Integer (N : Node_Id; V : Uint) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Typ : constant Entity_Id := Etype (N); |
| |
| begin |
| Rewrite (N, Make_Integer_Literal (Loc, Intval => V)); |
| Set_Etype (N, Typ); |
| end Rewrite_Integer; |
| |
| ------------------------------- |
| -- Set_And_Check_Static_Size -- |
| ------------------------------- |
| |
| procedure Set_And_Check_Static_Size |
| (E : Entity_Id; |
| Esiz : SO_Ref; |
| RM_Siz : SO_Ref) |
| is |
| SC : Node_Id; |
| |
| procedure Check_Size_Too_Small (Spec : Uint; Min : Uint); |
| -- Spec is the number of bit specified in the size clause, and |
| -- Min is the minimum computed size. An error is given that the |
| -- specified size is too small if Spec < Min, and in this case |
| -- both Esize and RM_Size are set to unknown in E. The error |
| -- message is posted on node SC. |
| |
| procedure Check_Unused_Bits (Spec : Uint; Max : Uint); |
| -- Spec is the number of bits specified in the size clause, and |
| -- Max is the maximum computed size. A warning is given about |
| -- unused bits if Spec > Max. This warning is posted on node SC. |
| |
| -------------------------- |
| -- Check_Size_Too_Small -- |
| -------------------------- |
| |
| procedure Check_Size_Too_Small (Spec : Uint; Min : Uint) is |
| begin |
| if Spec < Min then |
| Error_Msg_Uint_1 := Min; |
| Error_Msg_NE |
| ("size for & too small, minimum allowed is ^", SC, E); |
| Init_Esize (E); |
| Init_RM_Size (E); |
| end if; |
| end Check_Size_Too_Small; |
| |
| ----------------------- |
| -- Check_Unused_Bits -- |
| ----------------------- |
| |
| procedure Check_Unused_Bits (Spec : Uint; Max : Uint) is |
| begin |
| if Spec > Max then |
| Error_Msg_Uint_1 := Spec - Max; |
| Error_Msg_NE ("?^ bits of & unused", SC, E); |
| end if; |
| end Check_Unused_Bits; |
| |
| -- Start of processing for Set_And_Check_Static_Size |
| |
| begin |
| -- Case where Object_Size (Esize) is already set by a size clause |
| |
| if Known_Static_Esize (E) then |
| SC := Size_Clause (E); |
| |
| if No (SC) then |
| SC := Get_Attribute_Definition_Clause (E, Attribute_Object_Size); |
| end if; |
| |
| -- Perform checks on specified size against computed sizes |
| |
| if Present (SC) then |
| Check_Unused_Bits (Esize (E), Esiz); |
| Check_Size_Too_Small (Esize (E), RM_Siz); |
| end if; |
| end if; |
| |
| -- Case where Value_Size (RM_Size) is set by specific Value_Size |
| -- clause (we do not need to worry about Value_Size being set by |
| -- a Size clause, since that will have set Esize as well, and we |
| -- already took care of that case). |
| |
| if Known_Static_RM_Size (E) then |
| SC := Get_Attribute_Definition_Clause (E, Attribute_Value_Size); |
| |
| -- Perform checks on specified size against computed sizes |
| |
| if Present (SC) then |
| Check_Unused_Bits (RM_Size (E), Esiz); |
| Check_Size_Too_Small (RM_Size (E), RM_Siz); |
| end if; |
| end if; |
| |
| -- Set sizes if unknown |
| |
| if Unknown_Esize (E) then |
| Set_Esize (E, Esiz); |
| end if; |
| |
| if Unknown_RM_Size (E) then |
| Set_RM_Size (E, RM_Siz); |
| end if; |
| end Set_And_Check_Static_Size; |
| |
| ----------------------------- |
| -- Set_Composite_Alignment -- |
| ----------------------------- |
| |
| procedure Set_Composite_Alignment (E : Entity_Id) is |
| Siz : Uint; |
| Align : Nat; |
| |
| begin |
| if Unknown_Alignment (E) then |
| if Known_Static_Esize (E) then |
| Siz := Esize (E); |
| |
| elsif Unknown_Esize (E) |
| and then Known_Static_RM_Size (E) |
| then |
| Siz := RM_Size (E); |
| |
| else |
| return; |
| end if; |
| |
| -- Size is known, alignment is not set |
| |
| -- Reset alignment to match size if size is exactly 2, 4, or 8 bytes |
| |
| if Siz = 2 * System_Storage_Unit then |
| Align := 2; |
| elsif Siz = 4 * System_Storage_Unit then |
| Align := 4; |
| elsif Siz = 8 * System_Storage_Unit then |
| Align := 8; |
| |
| -- On VMS, also reset for odd "in between" sizes, e.g. a 17-bit |
| -- record is given an alignment of 4. This is more consistent with |
| -- what DEC Ada does. |
| |
| elsif OpenVMS_On_Target and then Siz > System_Storage_Unit then |
| |
| if Siz <= 2 * System_Storage_Unit then |
| Align := 2; |
| elsif Siz <= 4 * System_Storage_Unit then |
| Align := 4; |
| elsif Siz <= 8 * System_Storage_Unit then |
| Align := 8; |
| else |
| return; |
| end if; |
| |
| -- No special alignment fiddling needed |
| |
| else |
| return; |
| end if; |
| |
| -- Here Align is set to the proposed improved alignment |
| |
| if Align > Maximum_Alignment then |
| Align := Maximum_Alignment; |
| end if; |
| |
| -- Further processing for record types only to reduce the alignment |
| -- set by the above processing in some specific cases. We do not |
| -- do this for atomic records, since we need max alignment there. |
| |
| if Is_Record_Type (E) then |
| |
| -- For records, there is generally no point in setting alignment |
| -- higher than word size since we cannot do better than move by |
| -- words in any case |
| |
| if Align > System_Word_Size / System_Storage_Unit then |
| Align := System_Word_Size / System_Storage_Unit; |
| end if; |
| |
| -- Check components. If any component requires a higher |
| -- alignment, then we set that higher alignment in any case. |
| |
| declare |
| Comp : Entity_Id; |
| |
| begin |
| Comp := First_Component (E); |
| while Present (Comp) loop |
| if Known_Alignment (Etype (Comp)) then |
| declare |
| Calign : constant Uint := Alignment (Etype (Comp)); |
| |
| begin |
| -- The cases to worry about are when the alignment |
| -- of the component type is larger than the alignment |
| -- we have so far, and either there is no component |
| -- clause for the alignment, or the length set by |
| -- the component clause matches the alignment set. |
| |
| if Calign > Align |
| and then |
| (Unknown_Esize (Comp) |
| or else (Known_Static_Esize (Comp) |
| and then |
| Esize (Comp) = |
| Calign * System_Storage_Unit)) |
| then |
| Align := UI_To_Int (Calign); |
| end if; |
| end; |
| end if; |
| |
| Next_Component (Comp); |
| end loop; |
| end; |
| end if; |
| |
| -- Set chosen alignment |
| |
| Set_Alignment (E, UI_From_Int (Align)); |
| |
| if Known_Static_Esize (E) |
| and then Esize (E) < Align * System_Storage_Unit |
| then |
| Set_Esize (E, UI_From_Int (Align * System_Storage_Unit)); |
| end if; |
| end if; |
| end Set_Composite_Alignment; |
| |
| -------------------------- |
| -- Set_Discrete_RM_Size -- |
| -------------------------- |
| |
| procedure Set_Discrete_RM_Size (Def_Id : Entity_Id) is |
| FST : constant Entity_Id := First_Subtype (Def_Id); |
| |
| begin |
| -- All discrete types except for the base types in standard |
| -- are constrained, so indicate this by setting Is_Constrained. |
| |
| Set_Is_Constrained (Def_Id); |
| |
| -- We set generic types to have an unknown size, since the |
| -- representation of a generic type is irrelevant, in view |
| -- of the fact that they have nothing to do with code. |
| |
| if Is_Generic_Type (Root_Type (FST)) then |
| Set_RM_Size (Def_Id, Uint_0); |
| |
| -- If the subtype statically matches the first subtype, then |
| -- it is required to have exactly the same layout. This is |
| -- required by aliasing considerations. |
| |
| elsif Def_Id /= FST and then |
| Subtypes_Statically_Match (Def_Id, FST) |
| then |
| Set_RM_Size (Def_Id, RM_Size (FST)); |
| Set_Size_Info (Def_Id, FST); |
| |
| -- In all other cases the RM_Size is set to the minimum size. |
| -- Note that this routine is never called for subtypes for which |
| -- the RM_Size is set explicitly by an attribute clause. |
| |
| else |
| Set_RM_Size (Def_Id, UI_From_Int (Minimum_Size (Def_Id))); |
| end if; |
| end Set_Discrete_RM_Size; |
| |
| ------------------------ |
| -- Set_Prim_Alignment -- |
| ------------------------ |
| |
| procedure Set_Prim_Alignment (E : Entity_Id) is |
| begin |
| -- Do not set alignment for packed array types, unless we are doing |
| -- front end layout, because otherwise this is always handled in the |
| -- backend. |
| |
| if Is_Packed_Array_Type (E) and then not Frontend_Layout_On_Target then |
| return; |
| |
| -- If there is an alignment clause, then we respect it |
| |
| elsif Has_Alignment_Clause (E) then |
| return; |
| |
| -- If the size is not set, then don't attempt to set the alignment. This |
| -- happens in the backend layout case for access-to-subprogram types. |
| |
| elsif not Known_Static_Esize (E) then |
| return; |
| |
| -- For access types, do not set the alignment if the size is less than |
| -- the allowed minimum size. This avoids cascaded error messages. |
| |
| elsif Is_Access_Type (E) |
| and then Esize (E) < System_Address_Size |
| then |
| return; |
| end if; |
| |
| -- Here we calculate the alignment as the largest power of two |
| -- multiple of System.Storage_Unit that does not exceed either |
| -- the actual size of the type, or the maximum allowed alignment. |
| |
| declare |
| S : constant Int := |
| UI_To_Int (Esize (E)) / SSU; |
| A : Nat; |
| |
| begin |
| A := 1; |
| while 2 * A <= Ttypes.Maximum_Alignment |
| and then 2 * A <= S |
| loop |
| A := 2 * A; |
| end loop; |
| |
| -- Now we think we should set the alignment to A, but we |
| -- skip this if an alignment is already set to a value |
| -- greater than A (happens for derived types). |
| |
| -- However, if the alignment is known and too small it |
| -- must be increased, this happens in a case like: |
| |
| -- type R is new Character; |
| -- for R'Size use 16; |
| |
| -- Here the alignment inherited from Character is 1, but |
| -- it must be increased to 2 to reflect the increased size. |
| |
| if Unknown_Alignment (E) or else Alignment (E) < A then |
| Init_Alignment (E, A); |
| end if; |
| end; |
| end Set_Prim_Alignment; |
| |
| ---------------------- |
| -- SO_Ref_From_Expr -- |
| ---------------------- |
| |
| function SO_Ref_From_Expr |
| (Expr : Node_Id; |
| Ins_Type : Entity_Id; |
| Vtype : Entity_Id := Empty; |
| Make_Func : Boolean := False) |
| return Dynamic_SO_Ref |
| is |
| Loc : constant Source_Ptr := Sloc (Ins_Type); |
| |
| K : constant Entity_Id := |
| Make_Defining_Identifier (Loc, |
| Chars => New_Internal_Name ('K')); |
| |
| Decl : Node_Id; |
| |
| function Check_Node_V_Ref (N : Node_Id) return Traverse_Result; |
| -- Function used to check one node for reference to V |
| |
| function Has_V_Ref is new Traverse_Func (Check_Node_V_Ref); |
| -- Function used to traverse tree to check for reference to V |
| |
| ---------------------- |
| -- Check_Node_V_Ref -- |
| ---------------------- |
| |
| function Check_Node_V_Ref (N : Node_Id) return Traverse_Result is |
| begin |
| if Nkind (N) = N_Identifier then |
| if Chars (N) = Vname then |
| return Abandon; |
| else |
| return Skip; |
| end if; |
| |
| else |
| return OK; |
| end if; |
| end Check_Node_V_Ref; |
| |
| -- Start of processing for SO_Ref_From_Expr |
| |
| begin |
| -- Case of expression is an integer literal, in this case we just |
| -- return the value (which must always be non-negative, since size |
| -- and offset values can never be negative). |
| |
| if Nkind (Expr) = N_Integer_Literal then |
| pragma Assert (Intval (Expr) >= 0); |
| return Intval (Expr); |
| end if; |
| |
| -- Case where there is a reference to V, create function |
| |
| if Has_V_Ref (Expr) = Abandon then |
| |
| pragma Assert (Present (Vtype)); |
| Set_Is_Discrim_SO_Function (K); |
| |
| Decl := |
| Make_Subprogram_Body (Loc, |
| |
| Specification => |
| Make_Function_Specification (Loc, |
| Defining_Unit_Name => K, |
| Parameter_Specifications => New_List ( |
| Make_Parameter_Specification (Loc, |
| Defining_Identifier => |
| Make_Defining_Identifier (Loc, Chars => Vname), |
| Parameter_Type => |
| New_Occurrence_Of (Vtype, Loc))), |
| Subtype_Mark => |
| New_Occurrence_Of (Standard_Unsigned, Loc)), |
| |
| Declarations => Empty_List, |
| |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => New_List ( |
| Make_Return_Statement (Loc, |
| Expression => Expr)))); |
| |
| -- The caller requests that the expression be encapsulated in |
| -- a parameterless function. |
| |
| elsif Make_Func then |
| Decl := |
| Make_Subprogram_Body (Loc, |
| |
| Specification => |
| Make_Function_Specification (Loc, |
| Defining_Unit_Name => K, |
| Parameter_Specifications => Empty_List, |
| Subtype_Mark => New_Occurrence_Of (Standard_Unsigned, Loc)), |
| |
| Declarations => Empty_List, |
| |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => New_List ( |
| Make_Return_Statement (Loc, Expression => Expr)))); |
| |
| -- No reference to V and function not requested, so create a constant |
| |
| else |
| Decl := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => K, |
| Object_Definition => |
| New_Occurrence_Of (Standard_Unsigned, Loc), |
| Constant_Present => True, |
| Expression => Expr); |
| end if; |
| |
| Append_Freeze_Action (Ins_Type, Decl); |
| Analyze (Decl); |
| return Create_Dynamic_SO_Ref (K); |
| end SO_Ref_From_Expr; |
| |
| end Layout; |