| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- E X P _ P A K D -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2022, Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- |
| -- for more details. You should have received a copy of the GNU General -- |
| -- Public License distributed with GNAT; see file COPYING3. If not, go to -- |
| -- http://www.gnu.org/licenses for a complete copy of the license. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Atree; use Atree; |
| with Checks; use Checks; |
| with Einfo; use Einfo; |
| with Einfo.Entities; use Einfo.Entities; |
| with Einfo.Utils; use Einfo.Utils; |
| with Errout; use Errout; |
| with Exp_Dbug; use Exp_Dbug; |
| with Exp_Util; use Exp_Util; |
| with Layout; use Layout; |
| with Lib.Xref; use Lib.Xref; |
| with Namet; use Namet; |
| with Nlists; use Nlists; |
| with Nmake; use Nmake; |
| with Opt; use Opt; |
| with Sem; use Sem; |
| with Sem_Aux; use Sem_Aux; |
| with Sem_Ch3; use Sem_Ch3; |
| with Sem_Ch8; use Sem_Ch8; |
| with Sem_Ch13; use Sem_Ch13; |
| with Sem_Eval; use Sem_Eval; |
| with Sem_Res; use Sem_Res; |
| with Sem_Util; use Sem_Util; |
| with Sinfo; use Sinfo; |
| with Sinfo.Nodes; use Sinfo.Nodes; |
| with Sinfo.Utils; use Sinfo.Utils; |
| 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 Exp_Pakd is |
| |
| --------------------------- |
| -- Endian Considerations -- |
| --------------------------- |
| |
| -- As described in the specification, bit numbering in a packed array |
| -- is consistent with bit numbering in a record representation clause, |
| -- and hence dependent on the endianness of the machine: |
| |
| -- For little-endian machines, element zero is at the right hand end |
| -- (low order end) of a bit field. |
| |
| -- For big-endian machines, element zero is at the left hand end |
| -- (high order end) of a bit field. |
| |
| -- The shifts that are used to right justify a field therefore differ in |
| -- the two cases. For the little-endian case, we can simply use the bit |
| -- number (i.e. the element number * element size) as the count for a right |
| -- shift. For the big-endian case, we have to subtract the shift count from |
| -- an appropriate constant to use in the right shift. We use rotates |
| -- instead of shifts (which is necessary in the store case to preserve |
| -- other fields), and we expect that the backend will be able to change the |
| -- right rotate into a left rotate, avoiding the subtract, if the machine |
| -- architecture provides such an instruction. |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| procedure Compute_Linear_Subscript |
| (Atyp : Entity_Id; |
| N : Node_Id; |
| Subscr : out Node_Id); |
| -- Given a constrained array type Atyp, and an indexed component node N |
| -- referencing an array object of this type, build an expression of type |
| -- Standard.Integer representing the zero-based linear subscript value. |
| -- This expression includes any required range checks. |
| |
| function Compute_Number_Components |
| (N : Node_Id; |
| Typ : Entity_Id) return Node_Id; |
| -- Build an expression that multiplies the length of the dimensions of the |
| -- array, used to control array equality checks. |
| |
| procedure Convert_To_PAT_Type (Aexp : Node_Id); |
| -- Given an expression of a packed array type, builds a corresponding |
| -- expression whose type is the implementation type used to represent |
| -- the packed array. Aexp is analyzed and resolved on entry and on exit. |
| |
| procedure Get_Base_And_Bit_Offset |
| (N : Node_Id; |
| Base : out Node_Id; |
| Offset : out Node_Id); |
| -- Given a node N for a name which involves a packed array reference, |
| -- return the base object of the reference and build an expression of |
| -- type Standard.Integer representing the zero-based offset in bits |
| -- from Base'Address to the first bit of the reference. |
| |
| function Known_Aligned_Enough (Obj : Node_Id; Csiz : Nat) return Boolean; |
| -- There are two versions of the Set routines, the ones used when the |
| -- object is known to be sufficiently well aligned given the number of |
| -- bits, and the ones used when the object is not known to be aligned. |
| -- This routine is used to determine which set to use. Obj is a reference |
| -- to the object, and Csiz is the component size of the packed array. |
| -- True is returned if the alignment of object is known to be sufficient, |
| -- defined as 1 for odd bit sizes, 4 for bit sizes divisible by 4, and |
| -- 2 otherwise. |
| |
| function Make_Shift_Left (N : Node_Id; S : Node_Id) return Node_Id; |
| -- Build a left shift node, checking for the case of a shift count of zero |
| |
| function Make_Shift_Right (N : Node_Id; S : Node_Id) return Node_Id; |
| -- Build a right shift node, checking for the case of a shift count of zero |
| |
| function RJ_Unchecked_Convert_To |
| (Typ : Entity_Id; |
| Expr : Node_Id) return Node_Id; |
| -- The packed array code does unchecked conversions which in some cases |
| -- may involve non-discrete types with differing sizes. The semantics of |
| -- such conversions is potentially endianness dependent, and the effect |
| -- we want here for such a conversion is to do the conversion in size as |
| -- though numeric items are involved, and we extend or truncate on the |
| -- left side. This happens naturally in the little-endian case, but in |
| -- the big endian case we can get left justification, when what we want |
| -- is right justification. This routine does the unchecked conversion in |
| -- a stepwise manner to ensure that it gives the expected result. Hence |
| -- the name (RJ = Right justified). The parameters Typ and Expr are as |
| -- for the case of a normal Unchecked_Convert_To call. |
| |
| procedure Setup_Enumeration_Packed_Array_Reference (N : Node_Id); |
| -- This routine is called in the Get and Set case for arrays that are |
| -- packed but not bit-packed, meaning that they have at least one |
| -- subscript that is of an enumeration type with a non-standard |
| -- representation. This routine modifies the given node to properly |
| -- reference the corresponding packed array type. |
| |
| procedure Setup_Inline_Packed_Array_Reference |
| (N : Node_Id; |
| Atyp : Entity_Id; |
| Obj : in out Node_Id; |
| Cmask : out Uint; |
| Shift : out Node_Id); |
| -- This procedure performs common processing on the N_Indexed_Component |
| -- parameter given as N, whose prefix is a reference to a packed array. |
| -- This is used for the get and set when the component size is 1, 2, 4, |
| -- or for other component sizes when the packed array type is a modular |
| -- type (i.e. the cases that are handled with inline code). |
| -- |
| -- On entry: |
| -- |
| -- N is the N_Indexed_Component node for the packed array reference |
| -- |
| -- Atyp is the constrained array type (the actual subtype has been |
| -- computed if necessary to obtain the constraints, but this is still |
| -- the original array type, not the Packed_Array_Impl_Type value). |
| -- |
| -- Obj is the object which is to be indexed. It is always of type Atyp. |
| -- |
| -- On return: |
| -- |
| -- Obj is the object containing the desired bit field. It is of type |
| -- Unsigned, Long_Unsigned, or Long_Long_Unsigned, and is either the |
| -- entire value, for the small static case, or the proper selected byte |
| -- from the array in the large or dynamic case. This node is analyzed |
| -- and resolved on return. |
| -- |
| -- Shift is a node representing the shift count to be used in the |
| -- rotate right instruction that positions the field for access. |
| -- This node is analyzed and resolved on return. |
| -- |
| -- Cmask is a mask corresponding to the width of the component field. |
| -- Its value is 2 ** Csize - 1 (e.g. 2#1111# for component size of 4). |
| -- |
| -- Note: in some cases the call to this routine may generate actions |
| -- (for handling multi-use references and the generation of the packed |
| -- array type on the fly). Such actions are inserted into the tree |
| -- directly using Insert_Action. |
| |
| function Revert_Storage_Order (N : Node_Id) return Node_Id; |
| -- Perform appropriate justification and byte ordering adjustments for N, |
| -- an element of a packed array type, when both the component type and |
| -- the enclosing packed array type have reverse scalar storage order. |
| -- On little-endian targets, the value is left justified before byte |
| -- swapping. The Etype of the returned expression is an integer type of |
| -- an appropriate power-of-2 size. |
| |
| -------------------------- |
| -- Revert_Storage_Order -- |
| -------------------------- |
| |
| function Revert_Storage_Order (N : Node_Id) return Node_Id is |
| Loc : constant Source_Ptr := Sloc (N); |
| T : constant Entity_Id := Etype (N); |
| T_Size : constant Uint := RM_Size (T); |
| |
| Swap_RE : RE_Id; |
| Swap_F : Entity_Id; |
| Swap_T : Entity_Id; |
| -- Swapping function |
| |
| Arg : Node_Id; |
| Adjusted : Node_Id; |
| Shift : Uint; |
| |
| begin |
| if T_Size <= 8 then |
| |
| -- Array component size is less than a byte: no swapping needed |
| |
| Swap_F := Empty; |
| Swap_T := RTE (RE_Unsigned_8); |
| |
| else |
| -- Select byte swapping function depending on array component size |
| |
| if T_Size <= 16 then |
| Swap_RE := RE_Bswap_16; |
| |
| elsif T_Size <= 32 then |
| Swap_RE := RE_Bswap_32; |
| |
| elsif T_Size <= 64 then |
| Swap_RE := RE_Bswap_64; |
| |
| else pragma Assert (T_Size <= 128); |
| Swap_RE := RE_Bswap_128; |
| end if; |
| |
| Swap_F := RTE (Swap_RE); |
| Swap_T := Etype (Swap_F); |
| |
| end if; |
| |
| Shift := Esize (Swap_T) - T_Size; |
| |
| Arg := RJ_Unchecked_Convert_To (Swap_T, N); |
| |
| if not Bytes_Big_Endian and then Shift > Uint_0 then |
| Arg := |
| Make_Op_Shift_Left (Loc, |
| Left_Opnd => Arg, |
| Right_Opnd => Make_Integer_Literal (Loc, Shift)); |
| end if; |
| |
| if Present (Swap_F) then |
| Adjusted := |
| Make_Function_Call (Loc, |
| Name => New_Occurrence_Of (Swap_F, Loc), |
| Parameter_Associations => New_List (Arg)); |
| else |
| Adjusted := Arg; |
| end if; |
| |
| Set_Etype (Adjusted, Swap_T); |
| return Adjusted; |
| end Revert_Storage_Order; |
| |
| ------------------------------ |
| -- Compute_Linear_Subscript -- |
| ------------------------------ |
| |
| procedure Compute_Linear_Subscript |
| (Atyp : Entity_Id; |
| N : Node_Id; |
| Subscr : out Node_Id) |
| is |
| Loc : constant Source_Ptr := Sloc (N); |
| Oldsub : Node_Id; |
| Newsub : Node_Id; |
| Indx : Node_Id; |
| Styp : Entity_Id; |
| |
| begin |
| Subscr := Empty; |
| |
| -- Loop through dimensions |
| |
| Indx := First_Index (Atyp); |
| Oldsub := First (Expressions (N)); |
| |
| while Present (Indx) loop |
| Styp := Etype (Indx); |
| Newsub := Relocate_Node (Oldsub); |
| |
| -- Get expression for the subscript value. First, if Do_Range_Check |
| -- is set on a subscript, then we must do a range check against the |
| -- original bounds (not the bounds of the packed array type). We do |
| -- this by introducing a subtype conversion. |
| |
| if Do_Range_Check (Newsub) |
| and then Etype (Newsub) /= Styp |
| then |
| Newsub := Convert_To (Styp, Newsub); |
| end if; |
| |
| -- Now evolve the expression for the subscript. First convert |
| -- the subscript to be zero based and of an integer type. |
| |
| -- Case of integer type, where we just subtract to get lower bound |
| |
| if Is_Integer_Type (Styp) then |
| |
| -- If length of integer type is smaller than standard integer, |
| -- then we convert to integer first, then do the subtract |
| |
| -- Integer (subscript) - Integer (Styp'First) |
| |
| if Esize (Styp) < Standard_Integer_Size then |
| Newsub := |
| Make_Op_Subtract (Loc, |
| Left_Opnd => Convert_To (Standard_Integer, Newsub), |
| Right_Opnd => |
| Convert_To (Standard_Integer, |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Styp, Loc), |
| Attribute_Name => Name_First))); |
| |
| -- For larger integer types, subtract first, then convert to |
| -- integer, this deals with strange long long integer bounds. |
| |
| -- Integer (subscript - Styp'First) |
| |
| else |
| Newsub := |
| Convert_To (Standard_Integer, |
| Make_Op_Subtract (Loc, |
| Left_Opnd => Newsub, |
| Right_Opnd => |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Styp, Loc), |
| Attribute_Name => Name_First))); |
| end if; |
| |
| -- For the enumeration case, we have to use 'Pos to get the value |
| -- to work with before subtracting the lower bound. |
| |
| -- Integer (Styp'Pos (subscr)) - Integer (Styp'Pos (Styp'First)); |
| |
| -- This is not quite right for bizarre cases where the size of the |
| -- enumeration type is > Integer'Size bits due to rep clause ??? |
| |
| else |
| pragma Assert (Is_Enumeration_Type (Styp)); |
| |
| Newsub := |
| Make_Op_Subtract (Loc, |
| Left_Opnd => Convert_To (Standard_Integer, |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Styp, Loc), |
| Attribute_Name => Name_Pos, |
| Expressions => New_List (Newsub))), |
| |
| Right_Opnd => |
| Convert_To (Standard_Integer, |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Styp, Loc), |
| Attribute_Name => Name_Pos, |
| Expressions => New_List ( |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Styp, Loc), |
| Attribute_Name => Name_First))))); |
| end if; |
| |
| Set_Paren_Count (Newsub, 1); |
| |
| -- For the first subscript, we just copy that subscript value |
| |
| if No (Subscr) then |
| Subscr := Newsub; |
| |
| -- Otherwise, we must multiply what we already have by the current |
| -- stride and then add in the new value to the evolving subscript. |
| |
| else |
| Subscr := |
| Make_Op_Add (Loc, |
| Left_Opnd => |
| Make_Op_Multiply (Loc, |
| Left_Opnd => Subscr, |
| Right_Opnd => |
| Make_Attribute_Reference (Loc, |
| Attribute_Name => Name_Range_Length, |
| Prefix => New_Occurrence_Of (Styp, Loc))), |
| Right_Opnd => Newsub); |
| end if; |
| |
| -- Move to next subscript |
| |
| Next_Index (Indx); |
| Next (Oldsub); |
| end loop; |
| end Compute_Linear_Subscript; |
| |
| ------------------------------- |
| -- Compute_Number_Components -- |
| ------------------------------- |
| |
| function Compute_Number_Components |
| (N : Node_Id; |
| Typ : Entity_Id) return Node_Id |
| is |
| Loc : constant Source_Ptr := Sloc (N); |
| Len_Expr : Node_Id; |
| |
| begin |
| Len_Expr := |
| Make_Attribute_Reference (Loc, |
| Attribute_Name => Name_Length, |
| Prefix => New_Occurrence_Of (Typ, Loc), |
| Expressions => New_List (Make_Integer_Literal (Loc, 1))); |
| |
| for J in 2 .. Number_Dimensions (Typ) loop |
| Len_Expr := |
| Make_Op_Multiply (Loc, |
| Left_Opnd => Len_Expr, |
| Right_Opnd => |
| Make_Attribute_Reference (Loc, |
| Attribute_Name => Name_Length, |
| Prefix => New_Occurrence_Of (Typ, Loc), |
| Expressions => New_List (Make_Integer_Literal (Loc, J)))); |
| end loop; |
| |
| return Len_Expr; |
| end Compute_Number_Components; |
| |
| ------------------------- |
| -- Convert_To_PAT_Type -- |
| ------------------------- |
| |
| -- The PAT is always obtained from the actual subtype |
| |
| procedure Convert_To_PAT_Type (Aexp : Node_Id) is |
| Act_ST : Entity_Id; |
| |
| begin |
| Convert_To_Actual_Subtype (Aexp); |
| Act_ST := Underlying_Type (Etype (Aexp)); |
| Create_Packed_Array_Impl_Type (Act_ST); |
| |
| -- Just replace the etype with the packed array type. This works because |
| -- the expression will not be further analyzed, and Gigi considers the |
| -- two types equivalent in any case. |
| |
| -- This is not strictly the case ??? If the reference is an actual in |
| -- call, the expansion of the prefix is delayed, and must be reanalyzed, |
| -- see Reset_Packed_Prefix. On the other hand, if the prefix is a simple |
| -- array reference, reanalysis can produce spurious type errors when the |
| -- PAT type is replaced again with the original type of the array. Same |
| -- for the case of a dereference. Ditto for function calls: expansion |
| -- may introduce additional actuals which will trigger errors if call is |
| -- reanalyzed. The following is correct and minimal, but the handling of |
| -- more complex packed expressions in actuals is confused. Probably the |
| -- problem only remains for actuals in calls. |
| |
| Set_Etype (Aexp, Packed_Array_Impl_Type (Act_ST)); |
| |
| if Is_Entity_Name (Aexp) |
| or else |
| (Nkind (Aexp) = N_Indexed_Component |
| and then Is_Entity_Name (Prefix (Aexp))) |
| or else Nkind (Aexp) in N_Explicit_Dereference | N_Function_Call |
| then |
| Set_Analyzed (Aexp); |
| end if; |
| end Convert_To_PAT_Type; |
| |
| ----------------------------------- |
| -- Create_Packed_Array_Impl_Type -- |
| ----------------------------------- |
| |
| procedure Create_Packed_Array_Impl_Type (Typ : Entity_Id) is |
| Loc : constant Source_Ptr := Sloc (Typ); |
| Ctyp : constant Entity_Id := Component_Type (Typ); |
| Csize : constant Uint := Component_Size (Typ); |
| |
| Ancest : Entity_Id; |
| PB_Type : Entity_Id; |
| PASize : Uint := No_Uint; |
| Decl : Node_Id; |
| PAT : Entity_Id; |
| Len_Expr : Node_Id; |
| Len_Bits : Uint; |
| Bits_U1 : Node_Id; |
| PAT_High : Node_Id; |
| Btyp : Entity_Id; |
| Lit : Node_Id; |
| |
| procedure Install_PAT; |
| -- This procedure is called with Decl set to the declaration for the |
| -- packed array type. It creates the type and installs it as required. |
| |
| procedure Set_PB_Type; |
| -- Set PB_Type to [Rev_]Packed_Bytes{1,2,4} as required by the alignment |
| -- and the scalar storage order requirements (see documentation in the |
| -- spec of this package). |
| |
| ----------------- |
| -- Install_PAT -- |
| ----------------- |
| |
| procedure Install_PAT is |
| Pushed_Scope : Boolean := False; |
| |
| begin |
| -- We do not want to put the declaration we have created in the tree |
| -- since it is often hard, and sometimes impossible to find a proper |
| -- place for it (the impossible case arises for a packed array type |
| -- with bounds depending on the discriminant, a declaration cannot |
| -- be put inside the record, and the reference to the discriminant |
| -- cannot be outside the record). |
| |
| -- The solution is to analyze the declaration while temporarily |
| -- attached to the tree at an appropriate point, and then we install |
| -- the resulting type as an Itype in the packed array type field of |
| -- the original type, so that no explicit declaration is required. |
| |
| -- Note: the packed type is created in the scope of its parent type. |
| -- There are at least some cases where the current scope is deeper, |
| -- and so when this is the case, we temporarily reset the scope |
| -- for the definition. This is clearly safe, since the first use |
| -- of the packed array type will be the implicit reference from |
| -- the corresponding unpacked type when it is elaborated. |
| |
| if Is_Itype (Typ) then |
| Set_Parent (Decl, Associated_Node_For_Itype (Typ)); |
| else |
| Set_Parent (Decl, Declaration_Node (Typ)); |
| end if; |
| |
| if Scope (Typ) /= Current_Scope then |
| Push_Scope (Scope (Typ)); |
| Pushed_Scope := True; |
| end if; |
| |
| Set_Is_Itype (PAT, True); |
| Set_Is_Packed_Array_Impl_Type (PAT, True); |
| Set_Packed_Array_Impl_Type (Typ, PAT); |
| Analyze (Decl, Suppress => All_Checks); |
| |
| if Pushed_Scope then |
| Pop_Scope; |
| end if; |
| |
| -- Set Esize and RM_Size to the actual size of the packed object |
| -- Do not reset RM_Size if already set, as happens in the case of |
| -- a modular type. |
| |
| if Present (PASize) then |
| if not Known_Esize (PAT) then |
| Set_Esize (PAT, PASize); |
| end if; |
| |
| if not Known_RM_Size (PAT) then |
| Set_RM_Size (PAT, PASize); |
| end if; |
| end if; |
| |
| -- In the case of a modular type, make sure the alignment is |
| -- consistent with the Esize. |
| |
| if Is_Scalar_Type (PAT) then |
| while Alignment (PAT) * System_Storage_Unit < Esize (PAT) |
| and then Alignment (PAT) < Maximum_Alignment |
| loop |
| Set_Alignment (PAT, 2 * Alignment (PAT)); |
| end loop; |
| end if; |
| |
| -- Then, in all cases, make sure the opposite is also true |
| |
| Adjust_Esize_Alignment (PAT); |
| |
| -- Set remaining fields of packed array type |
| |
| Set_Parent (PAT, Empty); |
| Set_Associated_Node_For_Itype (PAT, Typ); |
| Set_Original_Array_Type (PAT, Typ); |
| |
| -- Propagate representation aspects |
| |
| Set_Is_Atomic (PAT, Is_Atomic (Typ)); |
| Set_Is_Independent (PAT, Is_Independent (Typ)); |
| Set_Is_Volatile (PAT, Is_Volatile (Typ)); |
| Set_Is_Volatile_Full_Access (PAT, Is_Volatile_Full_Access (Typ)); |
| Set_Treat_As_Volatile (PAT, Treat_As_Volatile (Typ)); |
| |
| -- We definitely do not want to delay freezing for packed array |
| -- types. This is of particular importance for the itypes that are |
| -- generated for record components depending on discriminants where |
| -- there is no place to put the freeze node. |
| |
| Set_Has_Delayed_Freeze (PAT, False); |
| Set_Has_Delayed_Freeze (Etype (PAT), False); |
| |
| -- If we did allocate a freeze node, then clear out the reference |
| -- since it is obsolete (should we delete the freeze node???) |
| |
| Set_Freeze_Node (PAT, Empty); |
| Set_Freeze_Node (Etype (PAT), Empty); |
| end Install_PAT; |
| |
| ----------------- |
| -- Set_PB_Type -- |
| ----------------- |
| |
| procedure Set_PB_Type is |
| begin |
| -- If the user has specified an explicit alignment for the |
| -- type or component, take it into account. |
| |
| if Csize <= 2 or else Csize = 4 or else Csize mod 2 /= 0 |
| or else (Known_Alignment (Typ) and then Alignment (Typ) = 1) |
| or else Component_Alignment (Typ) = Calign_Storage_Unit |
| then |
| if Reverse_Storage_Order (Typ) then |
| PB_Type := RTE (RE_Rev_Packed_Bytes1); |
| else |
| PB_Type := RTE (RE_Packed_Bytes1); |
| end if; |
| |
| elsif Csize mod 4 /= 0 |
| or else (Known_Alignment (Typ) and then Alignment (Typ) = 2) |
| then |
| if Reverse_Storage_Order (Typ) then |
| PB_Type := RTE (RE_Rev_Packed_Bytes2); |
| else |
| PB_Type := RTE (RE_Packed_Bytes2); |
| end if; |
| |
| else |
| if Reverse_Storage_Order (Typ) then |
| PB_Type := RTE (RE_Rev_Packed_Bytes4); |
| else |
| PB_Type := RTE (RE_Packed_Bytes4); |
| end if; |
| end if; |
| |
| -- The Rev_Packed_Bytes{1,2,4} types cannot be directly declared with |
| -- the reverse scalar storage order in System.Unsigned_Types because |
| -- their component type is aliased and the combination would then be |
| -- flagged as illegal by the compiler. Moreover changing the compiler |
| -- would not address the bootstrap path issue with earlier versions. |
| |
| Set_Reverse_Storage_Order (PB_Type, Reverse_Storage_Order (Typ)); |
| end Set_PB_Type; |
| |
| -- Start of processing for Create_Packed_Array_Impl_Type |
| |
| begin |
| -- If we already have a packed array type, nothing to do |
| |
| if Present (Packed_Array_Impl_Type (Typ)) then |
| return; |
| end if; |
| |
| -- If our immediate ancestor subtype is constrained, and it already has |
| -- a packed array type, and it has the same size, then just share the |
| -- same type, since the bounds must be the same. If the ancestor is not |
| -- an array type but a private type, as can happen with multiple |
| -- instantiations, create a new packed type, to avoid privacy issues. |
| |
| if Ekind (Typ) = E_Array_Subtype then |
| Ancest := Ancestor_Subtype (Typ); |
| |
| if Present (Ancest) |
| and then Is_Array_Type (Ancest) |
| and then Is_Constrained (Ancest) |
| and then Present (Packed_Array_Impl_Type (Ancest)) |
| and then Known_Esize (Typ) |
| and then Known_Esize (Ancest) |
| and then Esize (Typ) = Esize (Ancest) |
| then |
| Set_Packed_Array_Impl_Type (Typ, Packed_Array_Impl_Type (Ancest)); |
| return; |
| end if; |
| end if; |
| |
| -- We preset the result type size from the size of the original array |
| -- type, since this size clearly belongs to the packed array type. The |
| -- size of the conceptual unpacked type is always set to unknown. |
| |
| if Known_RM_Size (Typ) then |
| PASize := RM_Size (Typ); |
| end if; |
| |
| -- Case of an array where at least one index is of an enumeration |
| -- type with a non-standard representation, but the component size |
| -- is not appropriate for bit packing. This is the case where we |
| -- have Is_Packed set (we would never be in this unit otherwise), |
| -- but Is_Bit_Packed_Array is false. |
| |
| -- Note that if the component size is appropriate for bit packing, |
| -- then the circuit for the computation of the subscript properly |
| -- deals with the non-standard enumeration type case by taking the |
| -- Pos anyway. |
| |
| if not Is_Bit_Packed_Array (Typ) then |
| |
| -- Here we build a declaration: |
| |
| -- type tttP is array (index1, index2, ...) of component_type |
| |
| -- where index1, index2, are the index types. These are the same |
| -- as the index types of the original array, except for the non- |
| -- standard representation enumeration type case, where we have |
| -- two subcases. |
| |
| -- For the unconstrained array case, we use |
| |
| -- Natural range <> |
| |
| -- For the constrained case, we use |
| |
| -- Natural range Enum_Type'Pos (Enum_Type'First) .. |
| -- Enum_Type'Pos (Enum_Type'Last); |
| |
| -- Note that tttP is created even if no index subtype is a non |
| -- standard enumeration, because we still need to remove padding |
| -- normally inserted for component alignment. |
| |
| PAT := |
| Make_Defining_Identifier (Loc, |
| Chars => New_External_Name (Chars (Typ), 'P')); |
| |
| declare |
| Indexes : constant List_Id := New_List; |
| Indx : Node_Id; |
| Indx_Typ : Entity_Id; |
| Enum_Case : Boolean; |
| Typedef : Node_Id; |
| |
| begin |
| Indx := First_Index (Typ); |
| |
| while Present (Indx) loop |
| Indx_Typ := Etype (Indx); |
| |
| Enum_Case := Is_Enumeration_Type (Indx_Typ) |
| and then Has_Non_Standard_Rep (Indx_Typ); |
| |
| -- Unconstrained case |
| |
| if not Is_Constrained (Typ) then |
| if Enum_Case then |
| Indx_Typ := Standard_Natural; |
| end if; |
| |
| Append_To (Indexes, New_Occurrence_Of (Indx_Typ, Loc)); |
| |
| -- Constrained case |
| |
| else |
| if not Enum_Case then |
| Append_To (Indexes, New_Occurrence_Of (Indx_Typ, Loc)); |
| |
| else |
| Append_To (Indexes, |
| Make_Subtype_Indication (Loc, |
| Subtype_Mark => |
| New_Occurrence_Of (Standard_Natural, Loc), |
| Constraint => |
| Make_Range_Constraint (Loc, |
| Range_Expression => |
| Make_Range (Loc, |
| Low_Bound => |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of (Indx_Typ, Loc), |
| Attribute_Name => Name_Pos, |
| Expressions => New_List ( |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of (Indx_Typ, Loc), |
| Attribute_Name => Name_First))), |
| |
| High_Bound => |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of (Indx_Typ, Loc), |
| Attribute_Name => Name_Pos, |
| Expressions => New_List ( |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of (Indx_Typ, Loc), |
| Attribute_Name => Name_Last))))))); |
| |
| end if; |
| end if; |
| |
| Next_Index (Indx); |
| end loop; |
| |
| if not Is_Constrained (Typ) then |
| Typedef := |
| Make_Unconstrained_Array_Definition (Loc, |
| Subtype_Marks => Indexes, |
| Component_Definition => |
| Make_Component_Definition (Loc, |
| Aliased_Present => False, |
| Subtype_Indication => |
| New_Occurrence_Of (Ctyp, Loc))); |
| |
| else |
| Typedef := |
| Make_Constrained_Array_Definition (Loc, |
| Discrete_Subtype_Definitions => Indexes, |
| Component_Definition => |
| Make_Component_Definition (Loc, |
| Aliased_Present => False, |
| Subtype_Indication => |
| New_Occurrence_Of (Ctyp, Loc))); |
| end if; |
| |
| Decl := |
| Make_Full_Type_Declaration (Loc, |
| Defining_Identifier => PAT, |
| Type_Definition => Typedef); |
| end; |
| |
| Install_PAT; |
| |
| -- Propagate the reverse storage order flag to the base type |
| |
| Set_Reverse_Storage_Order (Etype (PAT), Reverse_Storage_Order (Typ)); |
| return; |
| |
| -- Case of bit-packing required for unconstrained array. We create |
| -- a subtype that is equivalent to use Packed_Bytes{1,2,4} as needed. |
| |
| elsif not Is_Constrained (Typ) then |
| |
| -- When generating standard DWARF (i.e when GNAT_Encodings is not |
| -- DWARF_GNAT_Encodings_All), the ___XP suffix will be stripped |
| -- by the back-end but generate it anyway to ease compiler debugging. |
| -- This will help to distinguish implementation types from original |
| -- packed arrays. |
| |
| PAT := |
| Make_Defining_Identifier (Loc, |
| Chars => Make_Packed_Array_Impl_Type_Name (Typ, Csize)); |
| |
| Set_PB_Type; |
| |
| Decl := |
| Make_Subtype_Declaration (Loc, |
| Defining_Identifier => PAT, |
| Subtype_Indication => New_Occurrence_Of (PB_Type, Loc)); |
| |
| Install_PAT; |
| return; |
| |
| -- Remaining code is for the case of bit-packing for constrained array |
| |
| -- The name of the packed array subtype is |
| |
| -- ttt___XPsss |
| |
| -- where sss is the component size in bits and ttt is the name of |
| -- the parent packed type. |
| |
| else |
| PAT := |
| Make_Defining_Identifier (Loc, |
| Chars => Make_Packed_Array_Impl_Type_Name (Typ, Csize)); |
| |
| -- Build an expression for the length of the array in bits. |
| -- This is the product of the length of each of the dimensions |
| |
| Len_Expr := Compute_Number_Components (Typ, Typ); |
| |
| -- Temporarily attach the length expression to the tree and analyze |
| -- and resolve it, so that we can test its value. We assume that the |
| -- total length fits in type Integer. This expression may involve |
| -- discriminants, so we treat it as a default/per-object expression. |
| |
| Set_Parent (Len_Expr, Typ); |
| Preanalyze_Spec_Expression (Len_Expr, Standard_Long_Long_Integer); |
| |
| -- Use a modular type if possible. We can do this if we have |
| -- static bounds, and the length is small enough, and the length |
| -- is not zero. We exclude the zero length case because the size |
| -- of things is always at least one, and the zero length object |
| -- would have an anomalous size. |
| |
| if Compile_Time_Known_Value (Len_Expr) then |
| Len_Bits := Expr_Value (Len_Expr) * Csize; |
| |
| -- Check for size known to be too large |
| |
| if Len_Bits > |
| Uint_2 ** (Standard_Integer_Size - 1) * System_Storage_Unit |
| then |
| if System_Storage_Unit = 8 then |
| Error_Msg_N |
| ("packed array size cannot exceed " & |
| "Integer''Last bytes", Typ); |
| else |
| Error_Msg_N |
| ("packed array size cannot exceed " & |
| "Integer''Last storage units", Typ); |
| end if; |
| |
| -- Reset length to arbitrary not too high value to continue |
| |
| Len_Expr := Make_Integer_Literal (Loc, 65535); |
| Analyze_And_Resolve (Len_Expr, Standard_Long_Long_Integer); |
| end if; |
| |
| -- We normally consider small enough to mean no larger than the |
| -- value of System_Max_Binary_Modulus_Power, checking that in the |
| -- case of values longer than word size, we have long shifts. |
| |
| if Len_Bits > 0 |
| and then |
| (Len_Bits <= System_Word_Size |
| or else (Len_Bits <= System_Max_Binary_Modulus_Power |
| and then Support_Long_Shifts_On_Target)) |
| then |
| -- We can use the modular type, it has the form: |
| |
| -- subtype tttPn is btyp |
| -- range 0 .. 2 ** ((Typ'Length (1) |
| -- * ... * Typ'Length (n)) * Csize) - 1; |
| |
| -- The bounds are statically known, and btyp is one of the |
| -- unsigned types, depending on the length. |
| |
| Btyp := Small_Integer_Type_For (Len_Bits, Uns => True); |
| Lit := Make_Integer_Literal (Loc, 2 ** Len_Bits - 1); |
| Set_Print_In_Hex (Lit); |
| |
| Decl := |
| Make_Subtype_Declaration (Loc, |
| Defining_Identifier => PAT, |
| Subtype_Indication => |
| Make_Subtype_Indication (Loc, |
| Subtype_Mark => New_Occurrence_Of (Btyp, Loc), |
| |
| Constraint => |
| Make_Range_Constraint (Loc, |
| Range_Expression => |
| Make_Range (Loc, |
| Low_Bound => |
| Make_Integer_Literal (Loc, 0), |
| High_Bound => Lit)))); |
| |
| if Present (PASize) then |
| PASize := Len_Bits; |
| end if; |
| |
| Install_PAT; |
| |
| -- Propagate a given alignment to the modular type. This can |
| -- cause it to be under-aligned, but that's OK. |
| |
| if Present (Alignment_Clause (Typ)) then |
| Set_Alignment (PAT, Alignment (Typ)); |
| end if; |
| |
| return; |
| end if; |
| end if; |
| |
| -- Could not use a modular type, for all other cases, we build |
| -- a packed array subtype: |
| |
| -- subtype tttPn is |
| -- System.Packed_Bytes{1,2,4} (0 .. (Bits + 7) / 8 - 1); |
| |
| -- Bits is the length of the array in bits |
| |
| Set_PB_Type; |
| |
| Bits_U1 := |
| Make_Op_Add (Loc, |
| Left_Opnd => |
| Make_Op_Multiply (Loc, |
| Left_Opnd => |
| Make_Integer_Literal (Loc, Csize), |
| Right_Opnd => Len_Expr), |
| |
| Right_Opnd => |
| Make_Integer_Literal (Loc, 7)); |
| |
| Set_Paren_Count (Bits_U1, 1); |
| |
| PAT_High := |
| Make_Op_Subtract (Loc, |
| Left_Opnd => |
| Make_Op_Divide (Loc, |
| Left_Opnd => Bits_U1, |
| Right_Opnd => Make_Integer_Literal (Loc, 8)), |
| Right_Opnd => Make_Integer_Literal (Loc, 1)); |
| |
| Decl := |
| Make_Subtype_Declaration (Loc, |
| Defining_Identifier => PAT, |
| Subtype_Indication => |
| Make_Subtype_Indication (Loc, |
| Subtype_Mark => New_Occurrence_Of (PB_Type, Loc), |
| Constraint => |
| Make_Index_Or_Discriminant_Constraint (Loc, |
| Constraints => New_List ( |
| Make_Range (Loc, |
| Low_Bound => |
| Make_Integer_Literal (Loc, 0), |
| High_Bound => |
| Convert_To (Standard_Integer, PAT_High)))))); |
| |
| Install_PAT; |
| |
| -- Currently the code in this unit requires that packed arrays |
| -- represented by non-modular arrays of bytes be on a byte |
| -- boundary for bit sizes handled by System.Pack_nn units. |
| -- That's because these units assume the array being accessed |
| -- starts on a byte boundary. |
| |
| if Get_Id (UI_To_Int (Csize)) /= RE_Null then |
| Set_Must_Be_On_Byte_Boundary (Typ); |
| end if; |
| end if; |
| end Create_Packed_Array_Impl_Type; |
| |
| ----------------------------------- |
| -- Expand_Bit_Packed_Element_Set -- |
| ----------------------------------- |
| |
| procedure Expand_Bit_Packed_Element_Set (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Lhs : constant Node_Id := Name (N); |
| |
| Ass_OK : constant Boolean := Assignment_OK (Lhs); |
| -- Used to preserve assignment OK status when assignment is rewritten |
| |
| Expr : Node_Id; |
| |
| Rhs : Node_Id := Expression (N); |
| -- Initially Rhs is the right hand side value, it will be replaced |
| -- later by an appropriate unchecked conversion for the assignment. |
| |
| Obj : Node_Id; |
| Atyp : Entity_Id; |
| PAT : Entity_Id; |
| Ctyp : Entity_Id; |
| Csiz : Int; |
| Cmask : Uint; |
| |
| Shift : Node_Id; |
| -- The expression for the shift value that is required |
| |
| Shift_Used : Boolean := False; |
| -- Set True if Shift has been used in the generated code at least once, |
| -- so that it must be duplicated if used again. |
| |
| New_Lhs : Node_Id; |
| New_Rhs : Node_Id; |
| |
| Rhs_Val_Known : Boolean; |
| Rhs_Val : Uint; |
| -- If the value of the right hand side as an integer constant is |
| -- known at compile time, Rhs_Val_Known is set True, and Rhs_Val |
| -- contains the value. Otherwise Rhs_Val_Known is set False, and |
| -- the Rhs_Val is undefined. |
| |
| function Get_Shift return Node_Id; |
| -- Function used to get the value of Shift, making sure that it |
| -- gets duplicated if the function is called more than once. |
| |
| --------------- |
| -- Get_Shift -- |
| --------------- |
| |
| function Get_Shift return Node_Id is |
| begin |
| -- If we used the shift value already, then duplicate it. We |
| -- set a temporary parent in case actions have to be inserted. |
| |
| if Shift_Used then |
| Set_Parent (Shift, N); |
| return Duplicate_Subexpr_No_Checks (Shift); |
| |
| -- If first time, use Shift unchanged, and set flag for first use |
| |
| else |
| Shift_Used := True; |
| return Shift; |
| end if; |
| end Get_Shift; |
| |
| -- Start of processing for Expand_Bit_Packed_Element_Set |
| |
| begin |
| pragma Assert (Is_Bit_Packed_Array (Etype (Prefix (Lhs)))); |
| |
| Obj := Relocate_Node (Prefix (Lhs)); |
| Convert_To_Actual_Subtype (Obj); |
| Atyp := Etype (Obj); |
| PAT := Packed_Array_Impl_Type (Atyp); |
| Ctyp := Component_Type (Atyp); |
| Csiz := UI_To_Int (Component_Size (Atyp)); |
| |
| -- We remove side effects, in case the rhs modifies the lhs, because we |
| -- are about to transform the rhs into an expression that first READS |
| -- the lhs, so we can do the necessary shifting and masking. Example: |
| -- "X(2) := F(...);" where F modifies X(3). Otherwise, the side effect |
| -- will be lost. |
| |
| Remove_Side_Effects (Rhs); |
| |
| -- We convert the right hand side to the proper subtype to ensure |
| -- that an appropriate range check is made (since the normal range |
| -- check from assignment will be lost in the transformations). This |
| -- conversion is analyzed immediately so that subsequent processing |
| -- can work with an analyzed Rhs (and e.g. look at its Etype) |
| |
| -- If the right-hand side is a string literal, create a temporary for |
| -- it, constant-folding is not ready to wrap the bit representation |
| -- of a string literal. |
| |
| if Nkind (Rhs) = N_String_Literal then |
| declare |
| Decl : Node_Id; |
| begin |
| Decl := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Make_Temporary (Loc, 'T', Rhs), |
| Object_Definition => New_Occurrence_Of (Ctyp, Loc), |
| Expression => New_Copy_Tree (Rhs)); |
| |
| Insert_Actions (N, New_List (Decl)); |
| Rhs := New_Occurrence_Of (Defining_Identifier (Decl), Loc); |
| end; |
| end if; |
| |
| Rhs := Convert_To (Ctyp, Rhs); |
| Set_Parent (Rhs, N); |
| |
| -- If we are building the initialization procedure for a packed array, |
| -- and Initialize_Scalars is enabled, each component assignment is an |
| -- out-of-range value by design. Compile this value without checks, |
| -- because a call to the array init_proc must not raise an exception. |
| |
| -- Condition is not consistent with description above, Within_Init_Proc |
| -- is True also when we are building the IP for a record or protected |
| -- type that has a packed array component??? |
| |
| if Within_Init_Proc |
| and then Initialize_Scalars |
| then |
| Analyze_And_Resolve (Rhs, Ctyp, Suppress => All_Checks); |
| else |
| Analyze_And_Resolve (Rhs, Ctyp); |
| end if; |
| |
| -- If any of the indices has a nonstandard representation, introduce |
| -- the proper Rep_To_Pos conversion, which in turn will generate index |
| -- checks when needed. We do this on a copy of the index expression, |
| -- rather that rewriting the LHS altogether. |
| |
| Expr := First (Expressions (Lhs)); |
| while Present (Expr) loop |
| declare |
| Expr_Typ : constant Entity_Id := Etype (Expr); |
| Loc : constant Source_Ptr := Sloc (Expr); |
| |
| Expr_Copy : Node_Id; |
| |
| begin |
| if Is_Enumeration_Type (Expr_Typ) |
| and then Has_Non_Standard_Rep (Expr_Typ) |
| then |
| Expr_Copy := |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Expr_Typ, Loc), |
| Attribute_Name => Name_Pos, |
| Expressions => New_List (Relocate_Node (Expr))); |
| Set_Parent (Expr_Copy, N); |
| Analyze_And_Resolve (Expr_Copy, Standard_Natural); |
| end if; |
| end; |
| |
| Next (Expr); |
| end loop; |
| |
| -- Case of component size 1,2,4 or any component size for the modular |
| -- case. These are the cases for which we can inline the code. |
| |
| if Csiz = 1 or else Csiz = 2 or else Csiz = 4 |
| or else (Present (PAT) and then Is_Modular_Integer_Type (PAT)) |
| then |
| Setup_Inline_Packed_Array_Reference (Lhs, Atyp, Obj, Cmask, Shift); |
| |
| -- The statement to be generated is: |
| |
| -- Obj := atyp!((Obj and Mask1) or (shift_left (rhs, Shift))) |
| |
| -- or in the case of a freestanding Reverse_Storage_Order object, |
| |
| -- Obj := Swap (atyp!((Swap (Obj) and Mask1) |
| -- or (shift_left (rhs, Shift)))) |
| |
| -- where Mask1 is obtained by shifting Cmask left Shift bits |
| -- and then complementing the result. |
| |
| -- the "and Mask1" is omitted if rhs is constant and all 1 bits |
| |
| -- the "or ..." is omitted if rhs is constant and all 0 bits |
| |
| -- rhs is converted to the appropriate type |
| |
| -- The result is converted back to the array type, since |
| -- otherwise we lose knowledge of the packed nature. |
| |
| -- Determine if right side is all 0 bits or all 1 bits |
| |
| if Compile_Time_Known_Value (Rhs) then |
| Rhs_Val := Expr_Rep_Value (Rhs); |
| Rhs_Val_Known := True; |
| |
| -- The following test catches the case of an unchecked conversion of |
| -- an integer literal. This results from optimizing aggregates of |
| -- packed types. |
| |
| elsif Nkind (Rhs) = N_Unchecked_Type_Conversion |
| and then Compile_Time_Known_Value (Expression (Rhs)) |
| then |
| Rhs_Val := Expr_Rep_Value (Expression (Rhs)); |
| Rhs_Val_Known := True; |
| |
| else |
| Rhs_Val := No_Uint; |
| Rhs_Val_Known := False; |
| end if; |
| |
| -- Some special checks for the case where the right hand value is |
| -- known at compile time. Basically we have to take care of the |
| -- implicit conversion to the subtype of the component object. |
| |
| if Rhs_Val_Known then |
| |
| -- If we have a biased component type then we must manually do the |
| -- biasing, since we are taking responsibility in this case for |
| -- constructing the exact bit pattern to be used. |
| |
| if Has_Biased_Representation (Ctyp) then |
| Rhs_Val := Rhs_Val - Expr_Rep_Value (Type_Low_Bound (Ctyp)); |
| end if; |
| |
| -- For a negative value, we manually convert the two's complement |
| -- value to a corresponding unsigned value, so that the proper |
| -- field width is maintained. If we did not do this, we would |
| -- get too many leading sign bits later on. |
| |
| if Rhs_Val < 0 then |
| Rhs_Val := 2 ** UI_From_Int (Csiz) + Rhs_Val; |
| end if; |
| end if; |
| |
| -- Now create copies removing side effects. Note that in some complex |
| -- cases, this may cause the fact that we have already set a packed |
| -- array type on Obj to get lost. So we save the type of Obj, and |
| -- make sure it is reset properly. |
| |
| declare |
| T : constant Entity_Id := Etype (Obj); |
| begin |
| New_Lhs := Duplicate_Subexpr (Obj, Name_Req => True); |
| New_Rhs := Duplicate_Subexpr_No_Checks (Obj); |
| Set_Etype (Obj, T); |
| Set_Etype (New_Lhs, T); |
| Set_Etype (New_Rhs, T); |
| end; |
| |
| -- First we deal with the "and" |
| |
| if not Rhs_Val_Known or else Rhs_Val /= Cmask then |
| declare |
| Mask1 : Node_Id; |
| Lit : Node_Id; |
| |
| begin |
| if Compile_Time_Known_Value (Shift) then |
| Mask1 := |
| Make_Integer_Literal (Loc, |
| Modulus (Etype (Obj)) - 1 - |
| (Cmask * (2 ** Expr_Value (Get_Shift)))); |
| Set_Print_In_Hex (Mask1); |
| |
| else |
| Lit := Make_Integer_Literal (Loc, Cmask); |
| Set_Print_In_Hex (Lit); |
| Mask1 := |
| Make_Op_Not (Loc, |
| Right_Opnd => Make_Shift_Left (Lit, Get_Shift)); |
| end if; |
| |
| New_Rhs := |
| Make_Op_And (Loc, |
| Left_Opnd => New_Rhs, |
| Right_Opnd => Mask1); |
| end; |
| end if; |
| |
| -- Then deal with the "or" |
| |
| if not Rhs_Val_Known or else Rhs_Val /= 0 then |
| declare |
| Or_Rhs : Node_Id; |
| |
| procedure Fixup_Rhs; |
| -- Adjust Rhs by bias if biased representation for components |
| -- or remove extraneous high order sign bits if signed. |
| |
| procedure Fixup_Rhs is |
| Etyp : constant Entity_Id := Etype (Rhs); |
| |
| begin |
| -- For biased case, do the required biasing by simply |
| -- converting to the biased subtype (the conversion |
| -- will generate the required bias). |
| |
| if Has_Biased_Representation (Ctyp) then |
| Rhs := Convert_To (Ctyp, Rhs); |
| |
| -- For a signed integer type that is not biased, generate |
| -- a conversion to unsigned to strip high order sign bits. |
| |
| elsif Is_Signed_Integer_Type (Ctyp) then |
| Rhs := Unchecked_Convert_To (RTE (Bits_Id (Csiz)), Rhs); |
| end if; |
| |
| -- Set Etype, since it can be referenced before the node is |
| -- completely analyzed. |
| |
| Set_Etype (Rhs, Etyp); |
| |
| -- We now need to do an unchecked conversion of the |
| -- result to the target type, but it is important that |
| -- this conversion be a right justified conversion and |
| -- not a left justified conversion. |
| |
| Rhs := RJ_Unchecked_Convert_To (Etype (Obj), Rhs); |
| end Fixup_Rhs; |
| |
| begin |
| if Rhs_Val_Known |
| and then Compile_Time_Known_Value (Get_Shift) |
| then |
| Or_Rhs := |
| Make_Integer_Literal (Loc, |
| Rhs_Val * (2 ** Expr_Value (Get_Shift))); |
| Set_Print_In_Hex (Or_Rhs); |
| |
| else |
| -- We have to convert the right hand side to Etype (Obj). |
| -- A special case arises if what we have now is a Val |
| -- attribute reference whose expression type is Etype (Obj). |
| -- This happens for assignments of fields from the same |
| -- array. In this case we get the required right hand side |
| -- by simply removing the inner attribute reference. |
| |
| if Nkind (Rhs) = N_Attribute_Reference |
| and then Attribute_Name (Rhs) = Name_Val |
| and then Etype (First (Expressions (Rhs))) = Etype (Obj) |
| then |
| Rhs := Relocate_Node (First (Expressions (Rhs))); |
| Fixup_Rhs; |
| |
| -- If the value of the right hand side is a known integer |
| -- value, then just replace it by an untyped constant, |
| -- which will be properly retyped when we analyze and |
| -- resolve the expression. |
| |
| elsif Rhs_Val_Known then |
| |
| -- Note that Rhs_Val has already been normalized to |
| -- be an unsigned value with the proper number of bits. |
| |
| Rhs := Make_Integer_Literal (Loc, Rhs_Val); |
| |
| -- Otherwise we need an unchecked conversion |
| |
| else |
| Fixup_Rhs; |
| end if; |
| |
| Or_Rhs := Make_Shift_Left (Rhs, Get_Shift); |
| end if; |
| |
| if Nkind (New_Rhs) = N_Op_And then |
| Set_Paren_Count (New_Rhs, 1); |
| Set_Etype (New_Rhs, Etype (Left_Opnd (New_Rhs))); |
| end if; |
| |
| New_Rhs := |
| Make_Op_Or (Loc, |
| Left_Opnd => New_Rhs, |
| Right_Opnd => Or_Rhs); |
| end; |
| end if; |
| |
| -- Now do the rewrite |
| |
| Rewrite (N, |
| Make_Assignment_Statement (Loc, |
| Name => New_Lhs, |
| Expression => |
| Unchecked_Convert_To (Etype (New_Lhs), New_Rhs))); |
| Set_Assignment_OK (Name (N), Ass_OK); |
| |
| -- All other component sizes for non-modular case |
| |
| else |
| -- We generate |
| |
| -- Set_nn (Arr'address, Subscr, Bits_nn!(Rhs)) |
| |
| -- where Subscr is the computed linear subscript |
| |
| declare |
| Bits_nn : constant Entity_Id := RTE (Bits_Id (Csiz)); |
| Set_nn : Entity_Id; |
| Subscr : Node_Id; |
| Atyp : Entity_Id; |
| Rev_SSO : Node_Id; |
| |
| begin |
| if No (Bits_nn) then |
| |
| -- Error, most likely High_Integrity_Mode restriction |
| |
| return; |
| end if; |
| |
| -- Acquire proper Set entity. We use the aligned or unaligned |
| -- case as appropriate. |
| |
| if Known_Aligned_Enough (Obj, Csiz) then |
| Set_nn := RTE (Set_Id (Csiz)); |
| else |
| Set_nn := RTE (SetU_Id (Csiz)); |
| end if; |
| |
| -- Now generate the set reference |
| |
| Obj := Relocate_Node (Prefix (Lhs)); |
| Convert_To_Actual_Subtype (Obj); |
| Atyp := Etype (Obj); |
| Compute_Linear_Subscript (Atyp, Lhs, Subscr); |
| |
| -- Set indication of whether the packed array has reverse SSO |
| |
| Rev_SSO := |
| New_Occurrence_Of |
| (Boolean_Literals (Reverse_Storage_Order (Atyp)), Loc); |
| |
| -- Below we must make the assumption that Obj is |
| -- at least byte aligned, since otherwise its address |
| -- cannot be taken. The assumption holds since the |
| -- only arrays that can be misaligned are small packed |
| -- arrays which are implemented as a modular type, and |
| -- that is not the case here. |
| |
| Rewrite (N, |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Set_nn, Loc), |
| Parameter_Associations => New_List ( |
| Make_Attribute_Reference (Loc, |
| Prefix => Obj, |
| Attribute_Name => Name_Address), |
| Subscr, |
| Unchecked_Convert_To (Bits_nn, Convert_To (Ctyp, Rhs)), |
| Rev_SSO))); |
| |
| end; |
| end if; |
| |
| Analyze (N, Suppress => All_Checks); |
| end Expand_Bit_Packed_Element_Set; |
| |
| ------------------------------------- |
| -- Expand_Packed_Address_Reference -- |
| ------------------------------------- |
| |
| procedure Expand_Packed_Address_Reference (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Base : Node_Id; |
| Offset : Node_Id; |
| |
| begin |
| -- We build an expression that has the form |
| |
| -- outer_object'Address |
| -- + (linear-subscript * component_size for each array reference |
| -- + field'Bit_Position for each record field |
| -- + ... |
| -- + ...) / Storage_Unit; |
| |
| Get_Base_And_Bit_Offset (Prefix (N), Base, Offset); |
| |
| Rewrite (N, |
| Unchecked_Convert_To (RTE (RE_Address), |
| Make_Op_Add (Loc, |
| Left_Opnd => |
| Unchecked_Convert_To (RTE (RE_Integer_Address), |
| Make_Attribute_Reference (Loc, |
| Prefix => Base, |
| Attribute_Name => Name_Address)), |
| |
| Right_Opnd => |
| Unchecked_Convert_To (RTE (RE_Integer_Address), |
| Make_Op_Divide (Loc, |
| Left_Opnd => Offset, |
| Right_Opnd => |
| Make_Integer_Literal (Loc, System_Storage_Unit)))))); |
| |
| Analyze_And_Resolve (N, RTE (RE_Address)); |
| end Expand_Packed_Address_Reference; |
| |
| --------------------------------- |
| -- Expand_Packed_Bit_Reference -- |
| --------------------------------- |
| |
| procedure Expand_Packed_Bit_Reference (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Base : Node_Id; |
| Offset : Node_Id; |
| |
| begin |
| -- We build an expression that has the form |
| |
| -- (linear-subscript * component_size for each array reference |
| -- + field'Bit_Position for each record field |
| -- + ... |
| -- + ...) mod Storage_Unit; |
| |
| Get_Base_And_Bit_Offset (Prefix (N), Base, Offset); |
| |
| Rewrite (N, |
| Unchecked_Convert_To (Standard_Natural, |
| Make_Op_Mod (Loc, |
| Left_Opnd => Offset, |
| Right_Opnd => Make_Integer_Literal (Loc, System_Storage_Unit)))); |
| |
| Analyze_And_Resolve (N, Standard_Natural); |
| end Expand_Packed_Bit_Reference; |
| |
| ------------------------------------ |
| -- Expand_Packed_Boolean_Operator -- |
| ------------------------------------ |
| |
| -- This routine expands "a op b" for the packed cases |
| |
| procedure Expand_Packed_Boolean_Operator (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Typ : constant Entity_Id := Etype (N); |
| L : constant Node_Id := Relocate_Node (Left_Opnd (N)); |
| R : Node_Id := Relocate_Node (Right_Opnd (N)); |
| |
| Ltyp : Entity_Id; |
| Rtyp : Entity_Id; |
| PAT : Entity_Id; |
| |
| begin |
| Convert_To_Actual_Subtype (L); |
| Convert_To_Actual_Subtype (R); |
| |
| Ensure_Defined (Etype (L), N); |
| Ensure_Defined (Etype (R), N); |
| |
| Apply_Length_Check (R, Etype (L)); |
| |
| Ltyp := Etype (L); |
| Rtyp := Etype (R); |
| |
| -- Deal with silly case of XOR where the subcomponent has a range |
| -- True .. True where an exception must be raised. |
| |
| if Nkind (N) = N_Op_Xor then |
| R := Duplicate_Subexpr (R); |
| Silly_Boolean_Array_Xor_Test (N, R, Rtyp); |
| end if; |
| |
| -- Now that silliness is taken care of, get packed array type |
| |
| Convert_To_PAT_Type (L); |
| Convert_To_PAT_Type (R); |
| |
| PAT := Etype (L); |
| |
| -- For the modular case, we expand a op b into |
| |
| -- rtyp!(pat!(a) op pat!(b)) |
| |
| -- where rtyp is the Etype of the left operand. Note that we do not |
| -- convert to the base type, since this would be unconstrained, and |
| -- hence not have a corresponding packed array type set. |
| |
| -- Note that both operands must be modular for this code to be used |
| |
| if Is_Modular_Integer_Type (PAT) |
| and then |
| Is_Modular_Integer_Type (Etype (R)) |
| then |
| declare |
| P : Node_Id; |
| |
| begin |
| if Nkind (N) = N_Op_And then |
| P := Make_Op_And (Loc, L, R); |
| |
| elsif Nkind (N) = N_Op_Or then |
| P := Make_Op_Or (Loc, L, R); |
| |
| else -- Nkind (N) = N_Op_Xor |
| P := Make_Op_Xor (Loc, L, R); |
| end if; |
| |
| Rewrite (N, Unchecked_Convert_To (Ltyp, P)); |
| end; |
| |
| -- For the array case, we insert the actions |
| |
| -- Result : Ltype; |
| |
| -- System.Bit_Ops.Bit_And/Or/Xor |
| -- (Left'Address, |
| -- Ltype'Length * Ltype'Component_Size; |
| -- Right'Address, |
| -- Rtype'Length * Rtype'Component_Size |
| -- Result'Address); |
| |
| -- where Left and Right are the Packed_Bytes{1,2,4} operands and |
| -- the second argument and fourth arguments are the lengths of the |
| -- operands in bits. Then we replace the expression by a reference |
| -- to Result. |
| |
| -- Note that if we are mixing a modular and array operand, everything |
| -- works fine, since we ensure that the modular representation has the |
| -- same physical layout as the array representation (that's what the |
| -- left justified modular stuff in the big-endian case is about). |
| |
| else |
| declare |
| Result_Ent : constant Entity_Id := Make_Temporary (Loc, 'T'); |
| E_Id : RE_Id; |
| |
| begin |
| if Nkind (N) = N_Op_And then |
| E_Id := RE_Bit_And; |
| |
| elsif Nkind (N) = N_Op_Or then |
| E_Id := RE_Bit_Or; |
| |
| else -- Nkind (N) = N_Op_Xor |
| E_Id := RE_Bit_Xor; |
| end if; |
| |
| Insert_Actions (N, New_List ( |
| |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Result_Ent, |
| Object_Definition => New_Occurrence_Of (Ltyp, Loc)), |
| |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (RTE (E_Id), Loc), |
| Parameter_Associations => New_List ( |
| |
| Make_Byte_Aligned_Attribute_Reference (Loc, |
| Prefix => L, |
| Attribute_Name => Name_Address), |
| |
| Make_Op_Multiply (Loc, |
| Left_Opnd => |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of |
| (Etype (First_Index (Ltyp)), Loc), |
| Attribute_Name => Name_Range_Length), |
| |
| Right_Opnd => |
| Make_Integer_Literal (Loc, Component_Size (Ltyp))), |
| |
| Make_Byte_Aligned_Attribute_Reference (Loc, |
| Prefix => R, |
| Attribute_Name => Name_Address), |
| |
| Make_Op_Multiply (Loc, |
| Left_Opnd => |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of |
| (Etype (First_Index (Rtyp)), Loc), |
| Attribute_Name => Name_Range_Length), |
| |
| Right_Opnd => |
| Make_Integer_Literal (Loc, Component_Size (Rtyp))), |
| |
| Make_Byte_Aligned_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Result_Ent, Loc), |
| Attribute_Name => Name_Address))))); |
| |
| Rewrite (N, |
| New_Occurrence_Of (Result_Ent, Loc)); |
| end; |
| end if; |
| |
| Analyze_And_Resolve (N, Typ, Suppress => All_Checks); |
| end Expand_Packed_Boolean_Operator; |
| |
| ------------------------------------- |
| -- Expand_Packed_Element_Reference -- |
| ------------------------------------- |
| |
| procedure Expand_Packed_Element_Reference (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Obj : Node_Id; |
| Atyp : Entity_Id; |
| PAT : Entity_Id; |
| Ctyp : Entity_Id; |
| Csiz : Int; |
| Shift : Node_Id; |
| Cmask : Uint; |
| Lit : Node_Id; |
| Arg : Node_Id; |
| |
| begin |
| -- If the node is an actual in a call, the prefix has not been fully |
| -- expanded, to account for the additional expansion for in-out actuals |
| -- (see expand_actuals for details). If the prefix itself is a packed |
| -- reference as well, we have to recurse to complete the transformation |
| -- of the prefix. |
| |
| if Nkind (Prefix (N)) = N_Indexed_Component |
| and then not Analyzed (Prefix (N)) |
| and then Is_Bit_Packed_Array (Etype (Prefix (Prefix (N)))) |
| then |
| Expand_Packed_Element_Reference (Prefix (N)); |
| end if; |
| |
| -- The prefix may be rewritten below as a conversion. If it is a source |
| -- entity generate reference to it now, to prevent spurious warnings |
| -- about unused entities. |
| |
| if Is_Entity_Name (Prefix (N)) |
| and then Comes_From_Source (Prefix (N)) |
| then |
| Generate_Reference (Entity (Prefix (N)), Prefix (N), 'r'); |
| end if; |
| |
| -- If not bit packed, we have the enumeration case, which is easily |
| -- dealt with (just adjust the subscripts of the indexed component) |
| |
| -- Note: this leaves the result as an indexed component, which is |
| -- still a variable, so can be used in the assignment case, as is |
| -- required in the enumeration case. |
| |
| if not Is_Bit_Packed_Array (Etype (Prefix (N))) then |
| Setup_Enumeration_Packed_Array_Reference (N); |
| return; |
| end if; |
| |
| -- Remaining processing is for the bit-packed case |
| |
| Obj := Relocate_Node (Prefix (N)); |
| Convert_To_Actual_Subtype (Obj); |
| Atyp := Etype (Obj); |
| PAT := Packed_Array_Impl_Type (Atyp); |
| Ctyp := Component_Type (Atyp); |
| Csiz := UI_To_Int (Component_Size (Atyp)); |
| |
| -- Case of component size 1,2,4 or any component size for the modular |
| -- case. These are the cases for which we can inline the code. |
| |
| if Csiz = 1 or else Csiz = 2 or else Csiz = 4 |
| or else (Present (PAT) and then Is_Modular_Integer_Type (PAT)) |
| then |
| Setup_Inline_Packed_Array_Reference (N, Atyp, Obj, Cmask, Shift); |
| Lit := Make_Integer_Literal (Loc, Cmask); |
| Set_Print_In_Hex (Lit); |
| |
| -- We generate a shift right to position the field, followed by a |
| -- masking operation to extract the bit field, and we finally do an |
| -- unchecked conversion to convert the result to the required target. |
| |
| -- Note that the unchecked conversion automatically deals with the |
| -- bias if we are dealing with a biased representation. What will |
| -- happen is that we temporarily generate the biased representation, |
| -- but almost immediately that will be converted to the original |
| -- unbiased component type, and the bias will disappear. |
| |
| Arg := |
| Make_Op_And (Loc, |
| Left_Opnd => Make_Shift_Right (Obj, Shift), |
| Right_Opnd => Lit); |
| Set_Etype (Arg, Ctyp); |
| |
| -- Component extraction is performed on a native endianness scalar |
| -- value: if Atyp has reverse storage order, then it has been byte |
| -- swapped, and if the component being extracted is itself of a |
| -- composite type with reverse storage order, then we need to swap |
| -- it back to its expected endianness after extraction. |
| |
| if Reverse_Storage_Order (Atyp) |
| and then (Is_Record_Type (Ctyp) or else Is_Array_Type (Ctyp)) |
| and then Reverse_Storage_Order (Ctyp) |
| then |
| Arg := Revert_Storage_Order (Arg); |
| end if; |
| |
| -- We needed to analyze this before we do the unchecked convert |
| -- below, but we need it temporarily attached to the tree for |
| -- this analysis (hence the temporary Set_Parent call). |
| |
| Set_Parent (Arg, Parent (N)); |
| Analyze_And_Resolve (Arg); |
| |
| Rewrite (N, RJ_Unchecked_Convert_To (Ctyp, Arg)); |
| |
| -- All other component sizes for non-modular case |
| |
| else |
| -- We generate |
| |
| -- Component_Type!(Get_nn (Arr'address, Subscr)) |
| |
| -- where Subscr is the computed linear subscript |
| |
| declare |
| Get_nn : Entity_Id; |
| Subscr : Node_Id; |
| Rev_SSO : constant Node_Id := |
| New_Occurrence_Of |
| (Boolean_Literals (Reverse_Storage_Order (Atyp)), Loc); |
| |
| begin |
| -- Acquire proper Get entity. We use the aligned or unaligned |
| -- case as appropriate. |
| |
| if Known_Aligned_Enough (Obj, Csiz) then |
| Get_nn := RTE (Get_Id (Csiz)); |
| else |
| Get_nn := RTE (GetU_Id (Csiz)); |
| end if; |
| |
| -- Now generate the get reference |
| |
| Compute_Linear_Subscript (Atyp, N, Subscr); |
| |
| -- Below we make the assumption that Obj is at least byte |
| -- aligned, since otherwise its address cannot be taken. |
| -- The assumption holds since the only arrays that can be |
| -- misaligned are small packed arrays which are implemented |
| -- as a modular type, and that is not the case here. |
| |
| Rewrite (N, |
| Unchecked_Convert_To (Ctyp, |
| Make_Function_Call (Loc, |
| Name => New_Occurrence_Of (Get_nn, Loc), |
| Parameter_Associations => New_List ( |
| Make_Attribute_Reference (Loc, |
| Prefix => Obj, |
| Attribute_Name => Name_Address), |
| Subscr, |
| Rev_SSO)))); |
| end; |
| end if; |
| |
| Analyze_And_Resolve (N, Ctyp, Suppress => All_Checks); |
| end Expand_Packed_Element_Reference; |
| |
| ---------------------- |
| -- Expand_Packed_Eq -- |
| ---------------------- |
| |
| -- Handles expansion of "=" on packed array types |
| |
| procedure Expand_Packed_Eq (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| L : constant Node_Id := Relocate_Node (Left_Opnd (N)); |
| R : constant Node_Id := Relocate_Node (Right_Opnd (N)); |
| |
| LLexpr : Node_Id; |
| RLexpr : Node_Id; |
| |
| Ltyp : Entity_Id; |
| Rtyp : Entity_Id; |
| PAT : Entity_Id; |
| |
| begin |
| Convert_To_Actual_Subtype (L); |
| Convert_To_Actual_Subtype (R); |
| Ltyp := Underlying_Type (Etype (L)); |
| Rtyp := Underlying_Type (Etype (R)); |
| |
| Convert_To_PAT_Type (L); |
| Convert_To_PAT_Type (R); |
| PAT := Etype (L); |
| |
| LLexpr := |
| Make_Op_Multiply (Loc, |
| Left_Opnd => Compute_Number_Components (N, Ltyp), |
| Right_Opnd => Make_Integer_Literal (Loc, Component_Size (Ltyp))); |
| |
| RLexpr := |
| Make_Op_Multiply (Loc, |
| Left_Opnd => Compute_Number_Components (N, Rtyp), |
| Right_Opnd => Make_Integer_Literal (Loc, Component_Size (Rtyp))); |
| |
| -- For the modular case, we transform the comparison to: |
| |
| -- Ltyp'Length = Rtyp'Length and then PAT!(L) = PAT!(R) |
| |
| -- where PAT is the packed array type. This works fine, since in the |
| -- modular case we guarantee that the unused bits are always zeroes. |
| -- We do have to compare the lengths because we could be comparing |
| -- two different subtypes of the same base type. We can only do this |
| -- if the PATs on both sides are modular (in which case they are |
| -- necessarily structurally the same -- same Modulus and so on); |
| -- otherwise, we have a case where the right operand is not of |
| -- compile time known size. |
| |
| if Is_Modular_Integer_Type (PAT) |
| and then Is_Modular_Integer_Type (Etype (R)) |
| then |
| pragma Assert (RM_Size (Etype (R)) = RM_Size (PAT)); |
| pragma Assert (Modulus (Etype (R)) = Modulus (PAT)); |
| |
| Rewrite (N, |
| Make_And_Then (Loc, |
| Left_Opnd => |
| Make_Op_Eq (Loc, |
| Left_Opnd => LLexpr, |
| Right_Opnd => RLexpr), |
| |
| Right_Opnd => |
| Make_Op_Eq (Loc, |
| Left_Opnd => L, |
| Right_Opnd => R))); |
| |
| -- For the non-modular case, we call a runtime routine |
| |
| -- System.Bit_Ops.Bit_Eq |
| -- (L'Address, L_Length, R'Address, R_Length) |
| |
| -- where PAT is the packed array type, and the lengths are the lengths |
| -- in bits of the original packed arrays. This routine takes care of |
| -- not comparing the unused bits in the last byte. |
| |
| else |
| Rewrite (N, |
| Make_Function_Call (Loc, |
| Name => New_Occurrence_Of (RTE (RE_Bit_Eq), Loc), |
| Parameter_Associations => New_List ( |
| Make_Byte_Aligned_Attribute_Reference (Loc, |
| Prefix => L, |
| Attribute_Name => Name_Address), |
| |
| LLexpr, |
| |
| Make_Byte_Aligned_Attribute_Reference (Loc, |
| Prefix => R, |
| Attribute_Name => Name_Address), |
| |
| RLexpr))); |
| end if; |
| |
| Analyze_And_Resolve (N, Standard_Boolean, Suppress => All_Checks); |
| end Expand_Packed_Eq; |
| |
| ----------------------- |
| -- Expand_Packed_Not -- |
| ----------------------- |
| |
| -- Handles expansion of "not" on packed array types |
| |
| procedure Expand_Packed_Not (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Typ : constant Entity_Id := Etype (N); |
| Opnd : constant Node_Id := Relocate_Node (Right_Opnd (N)); |
| |
| Rtyp : Entity_Id; |
| PAT : Entity_Id; |
| Lit : Node_Id; |
| Size : Unat; |
| |
| begin |
| Convert_To_Actual_Subtype (Opnd); |
| Rtyp := Etype (Opnd); |
| |
| -- Deal with silly False..False and True..True subtype case |
| |
| Silly_Boolean_Array_Not_Test (N, Rtyp); |
| |
| -- Now that the silliness is taken care of, get packed array type |
| |
| Convert_To_PAT_Type (Opnd); |
| PAT := Etype (Opnd); |
| |
| -- For the case where the packed array type is a modular type, "not A" |
| -- expands simply into: |
| |
| -- Rtyp!(PAT!(A) xor Mask) |
| |
| -- where PAT is the packed array type, Mask is a mask of all 1 bits of |
| -- length equal to the size of this packed type, and Rtyp is the actual |
| -- actual subtype of the operand. Preserve old behavior in case size is |
| -- not set. |
| |
| if Known_RM_Size (PAT) then |
| Size := RM_Size (PAT); |
| else |
| Size := Uint_0; |
| end if; |
| Lit := Make_Integer_Literal (Loc, 2 ** Size - 1); |
| Set_Print_In_Hex (Lit); |
| |
| if not Is_Array_Type (PAT) then |
| Rewrite (N, |
| Unchecked_Convert_To (Rtyp, |
| Make_Op_Xor (Loc, |
| Left_Opnd => Opnd, |
| Right_Opnd => Lit))); |
| |
| -- For the array case, we insert the actions |
| |
| -- Result : Typ; |
| |
| -- System.Bit_Ops.Bit_Not |
| -- (Opnd'Address, |
| -- Typ'Length * Typ'Component_Size, |
| -- Result'Address); |
| |
| -- where Opnd is the Packed_Bytes{1,2,4} operand and the second argument |
| -- is the length of the operand in bits. We then replace the expression |
| -- with a reference to Result. |
| |
| else |
| declare |
| Result_Ent : constant Entity_Id := Make_Temporary (Loc, 'T'); |
| |
| begin |
| Insert_Actions (N, New_List ( |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Result_Ent, |
| Object_Definition => New_Occurrence_Of (Rtyp, Loc)), |
| |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (RTE (RE_Bit_Not), Loc), |
| Parameter_Associations => New_List ( |
| Make_Byte_Aligned_Attribute_Reference (Loc, |
| Prefix => Opnd, |
| Attribute_Name => Name_Address), |
| |
| Make_Op_Multiply (Loc, |
| Left_Opnd => |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of |
| (Etype (First_Index (Rtyp)), Loc), |
| Attribute_Name => Name_Range_Length), |
| |
| Right_Opnd => |
| Make_Integer_Literal (Loc, Component_Size (Rtyp))), |
| |
| Make_Byte_Aligned_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Result_Ent, Loc), |
| Attribute_Name => Name_Address))))); |
| |
| Rewrite (N, New_Occurrence_Of (Result_Ent, Loc)); |
| end; |
| end if; |
| |
| Analyze_And_Resolve (N, Typ, Suppress => All_Checks); |
| end Expand_Packed_Not; |
| |
| ----------------------------- |
| -- Get_Base_And_Bit_Offset -- |
| ----------------------------- |
| |
| procedure Get_Base_And_Bit_Offset |
| (N : Node_Id; |
| Base : out Node_Id; |
| Offset : out Node_Id) |
| is |
| Loc : Source_Ptr; |
| Term : Node_Id; |
| Atyp : Entity_Id; |
| Subscr : Node_Id; |
| |
| begin |
| Base := N; |
| Offset := Empty; |
| |
| -- We build up an expression serially that has the form |
| |
| -- linear-subscript * component_size for each array reference |
| -- + field'Bit_Position for each record field |
| -- + ... |
| |
| loop |
| Loc := Sloc (Base); |
| |
| if Nkind (Base) = N_Indexed_Component then |
| Convert_To_Actual_Subtype (Prefix (Base)); |
| Atyp := Etype (Prefix (Base)); |
| Compute_Linear_Subscript (Atyp, Base, Subscr); |
| |
| Term := |
| Make_Op_Multiply (Loc, |
| Left_Opnd => Subscr, |
| Right_Opnd => |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Atyp, Loc), |
| Attribute_Name => Name_Component_Size)); |
| |
| elsif Nkind (Base) = N_Selected_Component then |
| Term := |
| Make_Attribute_Reference (Loc, |
| Prefix => Selector_Name (Base), |
| Attribute_Name => Name_Bit_Position); |
| |
| else |
| return; |
| end if; |
| |
| if No (Offset) then |
| Offset := Term; |
| |
| else |
| Offset := |
| Make_Op_Add (Loc, |
| Left_Opnd => Offset, |
| Right_Opnd => Term); |
| end if; |
| |
| Base := Prefix (Base); |
| end loop; |
| end Get_Base_And_Bit_Offset; |
| |
| ------------------------------------- |
| -- Involves_Packed_Array_Reference -- |
| ------------------------------------- |
| |
| function Involves_Packed_Array_Reference (N : Node_Id) return Boolean is |
| begin |
| if Nkind (N) = N_Indexed_Component |
| and then Is_Bit_Packed_Array (Etype (Prefix (N))) |
| then |
| return True; |
| |
| elsif Nkind (N) = N_Selected_Component then |
| return Involves_Packed_Array_Reference (Prefix (N)); |
| |
| else |
| return False; |
| end if; |
| end Involves_Packed_Array_Reference; |
| |
| -------------------------- |
| -- Known_Aligned_Enough -- |
| -------------------------- |
| |
| function Known_Aligned_Enough (Obj : Node_Id; Csiz : Nat) return Boolean is |
| Typ : constant Entity_Id := Etype (Obj); |
| |
| function In_Partially_Packed_Record (Comp : Entity_Id) return Boolean; |
| -- If the component is in a record that contains previous packed |
| -- components, consider it unaligned because the back-end might |
| -- choose to pack the rest of the record. Lead to less efficient code, |
| -- but safer vis-a-vis of back-end choices. |
| |
| -------------------------------- |
| -- In_Partially_Packed_Record -- |
| -------------------------------- |
| |
| function In_Partially_Packed_Record (Comp : Entity_Id) return Boolean is |
| Rec_Type : constant Entity_Id := Scope (Comp); |
| Prev_Comp : Entity_Id; |
| |
| begin |
| Prev_Comp := First_Entity (Rec_Type); |
| while Present (Prev_Comp) loop |
| if Is_Packed (Etype (Prev_Comp)) then |
| return True; |
| |
| elsif Prev_Comp = Comp then |
| return False; |
| end if; |
| |
| Next_Entity (Prev_Comp); |
| end loop; |
| |
| return False; |
| end In_Partially_Packed_Record; |
| |
| -- Start of processing for Known_Aligned_Enough |
| |
| begin |
| -- Odd bit sizes don't need alignment anyway |
| |
| if Csiz mod 2 = 1 then |
| return True; |
| |
| -- If we have a specified alignment, see if it is sufficient, if not |
| -- then we can't possibly be aligned enough in any case. |
| |
| elsif Known_Alignment (Etype (Obj)) then |
| -- Alignment required is 4 if size is a multiple of 4, and |
| -- 2 otherwise (e.g. 12 bits requires 4, 10 bits requires 2) |
| |
| if Alignment (Etype (Obj)) < 4 - (Csiz mod 4) then |
| return False; |
| end if; |
| end if; |
| |
| -- OK, alignment should be sufficient, if object is aligned |
| |
| -- If object is strictly aligned, then it is definitely aligned |
| |
| if Strict_Alignment (Typ) then |
| return True; |
| |
| -- Case of subscripted array reference |
| |
| elsif Nkind (Obj) = N_Indexed_Component then |
| |
| -- If we have a pointer to an array, then this is definitely |
| -- aligned, because pointers always point to aligned versions. |
| |
| if Is_Access_Type (Etype (Prefix (Obj))) then |
| return True; |
| |
| -- Otherwise, go look at the prefix |
| |
| else |
| return Known_Aligned_Enough (Prefix (Obj), Csiz); |
| end if; |
| |
| -- Case of record field |
| |
| elsif Nkind (Obj) = N_Selected_Component then |
| |
| -- What is significant here is whether the record type is packed |
| |
| if Is_Record_Type (Etype (Prefix (Obj))) |
| and then Is_Packed (Etype (Prefix (Obj))) |
| then |
| return False; |
| |
| -- Or the component has a component clause which might cause |
| -- the component to become unaligned (we can't tell if the |
| -- backend is doing alignment computations). |
| |
| elsif Present (Component_Clause (Entity (Selector_Name (Obj)))) then |
| return False; |
| |
| elsif In_Partially_Packed_Record (Entity (Selector_Name (Obj))) then |
| return False; |
| |
| -- In all other cases, go look at prefix |
| |
| else |
| return Known_Aligned_Enough (Prefix (Obj), Csiz); |
| end if; |
| |
| elsif Nkind (Obj) = N_Type_Conversion then |
| return Known_Aligned_Enough (Expression (Obj), Csiz); |
| |
| -- For a formal parameter, it is safer to assume that it is not |
| -- aligned, because the formal may be unconstrained while the actual |
| -- is constrained. In this situation, a small constrained packed |
| -- array, represented in modular form, may be unaligned. |
| |
| elsif Is_Entity_Name (Obj) then |
| return not Is_Formal (Entity (Obj)); |
| else |
| |
| -- If none of the above, must be aligned |
| return True; |
| end if; |
| end Known_Aligned_Enough; |
| |
| --------------------- |
| -- Make_Shift_Left -- |
| --------------------- |
| |
| function Make_Shift_Left (N : Node_Id; S : Node_Id) return Node_Id is |
| Nod : Node_Id; |
| |
| begin |
| if Compile_Time_Known_Value (S) and then Expr_Value (S) = 0 then |
| return N; |
| else |
| Nod := |
| Make_Op_Shift_Left (Sloc (N), |
| Left_Opnd => N, |
| Right_Opnd => S); |
| Set_Shift_Count_OK (Nod, True); |
| return Nod; |
| end if; |
| end Make_Shift_Left; |
| |
| ---------------------- |
| -- Make_Shift_Right -- |
| ---------------------- |
| |
| function Make_Shift_Right (N : Node_Id; S : Node_Id) return Node_Id is |
| Nod : Node_Id; |
| |
| begin |
| if Compile_Time_Known_Value (S) and then Expr_Value (S) = 0 then |
| return N; |
| else |
| Nod := |
| Make_Op_Shift_Right (Sloc (N), |
| Left_Opnd => N, |
| Right_Opnd => S); |
| Set_Shift_Count_OK (Nod, True); |
| return Nod; |
| end if; |
| end Make_Shift_Right; |
| |
| ----------------------------- |
| -- RJ_Unchecked_Convert_To -- |
| ----------------------------- |
| |
| function RJ_Unchecked_Convert_To |
| (Typ : Entity_Id; |
| Expr : Node_Id) return Node_Id |
| is |
| Source_Typ : constant Entity_Id := Etype (Expr); |
| Target_Typ : constant Entity_Id := Typ; |
| |
| Src : Node_Id := Expr; |
| |
| Source_Siz : Nat; |
| Target_Siz : Nat; |
| |
| begin |
| Source_Siz := UI_To_Int (RM_Size (Source_Typ)); |
| Target_Siz := UI_To_Int (RM_Size (Target_Typ)); |
| |
| -- For a little-endian target type stored byte-swapped on a |
| -- big-endian machine, do not mask to Target_Siz bits. |
| |
| if Bytes_Big_Endian |
| and then (Is_Record_Type (Target_Typ) |
| or else |
| Is_Array_Type (Target_Typ)) |
| and then Reverse_Storage_Order (Target_Typ) |
| then |
| Source_Siz := Target_Siz; |
| end if; |
| |
| -- First step, if the source type is not a discrete type, then we first |
| -- convert to a modular type of the source length, since otherwise, on |
| -- a big-endian machine, we get left-justification. We do it for little- |
| -- endian machines as well, because there might be junk bits that are |
| -- not cleared if the type is not numeric. This can be done only if the |
| -- source siz is different from 0 (i.e. known), otherwise we must trust |
| -- the type declarations (case of non-discrete components). |
| |
| if Source_Siz /= 0 |
| and then Source_Siz /= Target_Siz |
| and then not Is_Discrete_Type (Source_Typ) |
| then |
| Src := Unchecked_Convert_To (RTE (Bits_Id (Source_Siz)), Src); |
| end if; |
| |
| -- In the big endian case, if the lengths of the two types differ, then |
| -- we must worry about possible left justification in the conversion, |
| -- and avoiding that is what this is all about. |
| |
| if Bytes_Big_Endian and then Source_Siz /= Target_Siz then |
| |
| -- Next step. If the target is not a discrete type, then we first |
| -- convert to a modular type of the target length, since otherwise, |
| -- on a big-endian machine, we get left-justification. |
| |
| if not Is_Discrete_Type (Target_Typ) then |
| Src := Unchecked_Convert_To (RTE (Bits_Id (Target_Siz)), Src); |
| end if; |
| end if; |
| |
| -- And now we can do the final conversion to the target type |
| |
| return Unchecked_Convert_To (Target_Typ, Src); |
| end RJ_Unchecked_Convert_To; |
| |
| ---------------------------------------------- |
| -- Setup_Enumeration_Packed_Array_Reference -- |
| ---------------------------------------------- |
| |
| -- All we have to do here is to find the subscripts that correspond to the |
| -- index positions that have non-standard enumeration types and insert a |
| -- Pos attribute to get the proper subscript value. |
| |
| -- Finally the prefix must be uncheck-converted to the corresponding packed |
| -- array type. |
| |
| -- Note that the component type is unchanged, so we do not need to fiddle |
| -- with the types (Gigi always automatically takes the packed array type if |
| -- it is set, as it will be in this case). |
| |
| procedure Setup_Enumeration_Packed_Array_Reference (N : Node_Id) is |
| Pfx : constant Node_Id := Prefix (N); |
| Typ : constant Entity_Id := Etype (N); |
| Exprs : constant List_Id := Expressions (N); |
| Expr : Node_Id; |
| |
| begin |
| -- If the array is unconstrained, then we replace the array reference |
| -- with its actual subtype. This actual subtype will have a packed array |
| -- type with appropriate bounds. |
| |
| if not Is_Constrained (Packed_Array_Impl_Type (Etype (Pfx))) then |
| Convert_To_Actual_Subtype (Pfx); |
| end if; |
| |
| Expr := First (Exprs); |
| while Present (Expr) loop |
| declare |
| Loc : constant Source_Ptr := Sloc (Expr); |
| Expr_Typ : constant Entity_Id := Etype (Expr); |
| |
| begin |
| if Is_Enumeration_Type (Expr_Typ) |
| and then Has_Non_Standard_Rep (Expr_Typ) |
| then |
| Rewrite (Expr, |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Expr_Typ, Loc), |
| Attribute_Name => Name_Pos, |
| Expressions => New_List (Relocate_Node (Expr)))); |
| Analyze_And_Resolve (Expr, Standard_Natural); |
| end if; |
| end; |
| |
| Next (Expr); |
| end loop; |
| |
| Rewrite (N, |
| Make_Indexed_Component (Sloc (N), |
| Prefix => |
| Unchecked_Convert_To (Packed_Array_Impl_Type (Etype (Pfx)), Pfx), |
| Expressions => Exprs)); |
| |
| Analyze_And_Resolve (N, Typ); |
| end Setup_Enumeration_Packed_Array_Reference; |
| |
| ----------------------------------------- |
| -- Setup_Inline_Packed_Array_Reference -- |
| ----------------------------------------- |
| |
| procedure Setup_Inline_Packed_Array_Reference |
| (N : Node_Id; |
| Atyp : Entity_Id; |
| Obj : in out Node_Id; |
| Cmask : out Uint; |
| Shift : out Node_Id) |
| is |
| Loc : constant Source_Ptr := Sloc (N); |
| PAT : Entity_Id; |
| Otyp : Entity_Id; |
| Csiz : Uint; |
| Osiz : Uint; |
| |
| begin |
| Csiz := Component_Size (Atyp); |
| |
| Convert_To_PAT_Type (Obj); |
| PAT := Etype (Obj); |
| |
| Cmask := 2 ** Csiz - 1; |
| |
| if Is_Array_Type (PAT) then |
| Otyp := Component_Type (PAT); |
| Osiz := Component_Size (PAT); |
| |
| else |
| Otyp := PAT; |
| |
| -- In the case where the PAT is a modular type, we want the actual |
| -- size in bits of the modular value we use. This is neither the |
| -- Object_Size nor the Value_Size, either of which may have been |
| -- reset to strange values, but rather the minimum size. Note that |
| -- since this is a modular type with full range, the issue of |
| -- biased representation does not arise. |
| |
| Osiz := UI_From_Int (Minimum_Size (Otyp)); |
| end if; |
| |
| Compute_Linear_Subscript (Atyp, N, Shift); |
| |
| -- If the component size is not 1, then the subscript must be multiplied |
| -- by the component size to get the shift count. |
| |
| if Csiz /= 1 then |
| Shift := |
| Make_Op_Multiply (Loc, |
| Left_Opnd => Make_Integer_Literal (Loc, Csiz), |
| Right_Opnd => Shift); |
| end if; |
| |
| -- If we have the array case, then this shift count must be broken down |
| -- into a byte subscript, and a shift within the byte. |
| |
| if Is_Array_Type (PAT) then |
| |
| declare |
| New_Shift : Node_Id; |
| |
| begin |
| -- We must analyze shift, since we will duplicate it |
| |
| Set_Parent (Shift, N); |
| Analyze_And_Resolve |
| (Shift, Standard_Integer, Suppress => All_Checks); |
| |
| -- The shift count within the word is |
| -- shift mod Osiz |
| |
| New_Shift := |
| Make_Op_Mod (Loc, |
| Left_Opnd => Duplicate_Subexpr (Shift), |
| Right_Opnd => Make_Integer_Literal (Loc, Osiz)); |
| |
| -- The subscript to be used on the PAT array is |
| -- shift / Osiz |
| |
| Obj := |
| Make_Indexed_Component (Loc, |
| Prefix => Obj, |
| Expressions => New_List ( |
| Make_Op_Divide (Loc, |
| Left_Opnd => Duplicate_Subexpr (Shift), |
| Right_Opnd => Make_Integer_Literal (Loc, Osiz)))); |
| |
| Shift := New_Shift; |
| end; |
| |
| -- For the modular integer case, the object to be manipulated is the |
| -- entire array, so Obj is unchanged. Note that we will reset its type |
| -- to PAT before returning to the caller. |
| |
| else |
| null; |
| end if; |
| |
| -- The one remaining step is to modify the shift count for the |
| -- big-endian case. Consider the following example in a byte: |
| |
| -- xxxxxxxx bits of byte |
| -- vvvvvvvv bits of value |
| -- 33221100 little-endian numbering |
| -- 00112233 big-endian numbering |
| |
| -- Here we have the case of 2-bit fields |
| |
| -- For the little-endian case, we already have the proper shift count |
| -- set, e.g. for element 2, the shift count is 2*2 = 4. |
| |
| -- For the big endian case, we have to adjust the shift count, computing |
| -- it as (N - F) - Shift, where N is the number of bits in an element of |
| -- the array used to implement the packed array, F is the number of bits |
| -- in a source array element, and Shift is the count so far computed. |
| |
| -- We also have to adjust if the storage order is reversed |
| |
| if Bytes_Big_Endian xor Reverse_Storage_Order (Base_Type (Atyp)) then |
| Shift := |
| Make_Op_Subtract (Loc, |
| Left_Opnd => Make_Integer_Literal (Loc, Osiz - Csiz), |
| Right_Opnd => Shift); |
| end if; |
| |
| Set_Parent (Shift, N); |
| Set_Parent (Obj, N); |
| Analyze_And_Resolve (Obj, Otyp, Suppress => All_Checks); |
| Analyze_And_Resolve (Shift, Standard_Integer, Suppress => All_Checks); |
| |
| -- Make sure final type of object is the appropriate packed type |
| |
| Set_Etype (Obj, Otyp); |
| |
| end Setup_Inline_Packed_Array_Reference; |
| |
| end Exp_Pakd; |