| .. role:: switch(samp) |
| |
| .. _Representation_Clauses_and_Pragmas: |
| |
| ********************************** |
| Representation Clauses and Pragmas |
| ********************************** |
| |
| .. index:: Representation Clauses |
| |
| .. index:: Representation Clause |
| |
| .. index:: Representation Pragma |
| |
| .. index:: Pragma, representation |
| |
| This section describes the representation clauses accepted by GNAT, and |
| their effect on the representation of corresponding data objects. |
| |
| GNAT fully implements Annex C (Systems Programming). This means that all |
| the implementation advice sections in chapter 13 are fully implemented. |
| However, these sections only require a minimal level of support for |
| representation clauses. GNAT provides much more extensive capabilities, |
| and this section describes the additional capabilities provided. |
| |
| .. _Alignment_Clauses: |
| |
| Alignment Clauses |
| ================= |
| |
| .. index:: Alignment Clause |
| |
| GNAT requires that all alignment clauses specify 0 or a power of 2, and |
| all default alignments are always a power of 2. Specifying 0 is the |
| same as specifying 1. |
| |
| The default alignment values are as follows: |
| |
| * *Elementary Types*. |
| |
| For elementary types, the alignment is the minimum of the actual size of |
| objects of the type divided by ``Storage_Unit``, |
| and the maximum alignment supported by the target. |
| (This maximum alignment is given by the GNAT-specific attribute |
| ``Standard'Maximum_Alignment``; see :ref:`Attribute_Maximum_Alignment`.) |
| |
| .. index:: Maximum_Alignment attribute |
| |
| For example, for type ``Long_Float``, the object size is 8 bytes, and the |
| default alignment will be 8 on any target that supports alignments |
| this large, but on some targets, the maximum alignment may be smaller |
| than 8, in which case objects of type ``Long_Float`` will be maximally |
| aligned. |
| |
| * *Arrays*. |
| |
| For arrays, the alignment is equal to the alignment of the component type |
| for the normal case where no packing or component size is given. If the |
| array is packed, and the packing is effective (see separate section on |
| packed arrays), then the alignment will be either 4, 2, or 1 for long packed |
| arrays or arrays whose length is not known at compile time, depending on |
| whether the component size is divisible by 4, 2, or is odd. For short packed |
| arrays, which are handled internally as modular types, the alignment |
| will be as described for elementary types, e.g. a packed array of length |
| 31 bits will have an object size of four bytes, and an alignment of 4. |
| |
| * *Records*. |
| |
| For the normal unpacked case, the alignment of a record is equal to |
| the maximum alignment of any of its components. For tagged records, this |
| includes the implicit access type used for the tag. If a pragma ``Pack`` |
| is used and all components are packable (see separate section on pragma |
| ``Pack``), then the resulting alignment is 1, unless the layout of the |
| record makes it profitable to increase it. |
| |
| A special case is when: |
| |
| * the size of the record is given explicitly, or a |
| full record representation clause is given, and |
| |
| * the size of the record is 2, 4, or 8 bytes. |
| |
| In this case, an alignment is chosen to match the |
| size of the record. For example, if we have: |
| |
| .. code-block:: ada |
| |
| type Small is record |
| A, B : Character; |
| end record; |
| for Small'Size use 16; |
| |
| then the default alignment of the record type ``Small`` is 2, not 1. This |
| leads to more efficient code when the record is treated as a unit, and also |
| allows the type to specified as ``Atomic`` on architectures requiring |
| strict alignment. |
| |
| An alignment clause may specify a larger alignment than the default value |
| up to some maximum value dependent on the target (obtainable by using the |
| attribute reference ``Standard'Maximum_Alignment``). It may also specify |
| a smaller alignment than the default value for enumeration, integer and |
| fixed point types, as well as for record types, for example |
| |
| .. code-block:: ada |
| |
| type V is record |
| A : Integer; |
| end record; |
| |
| for V'alignment use 1; |
| |
| .. index:: Alignment, default |
| |
| The default alignment for the type ``V`` is 4, as a result of the |
| Integer field in the record, but it is permissible, as shown, to |
| override the default alignment of the record with a smaller value. |
| |
| .. index:: Alignment, subtypes |
| |
| Note that according to the Ada standard, an alignment clause applies only |
| to the first named subtype. If additional subtypes are declared, then the |
| compiler is allowed to choose any alignment it likes, and there is no way |
| to control this choice. Consider: |
| |
| .. code-block:: ada |
| |
| type R is range 1 .. 10_000; |
| for R'Alignment use 1; |
| subtype RS is R range 1 .. 1000; |
| |
| The alignment clause specifies an alignment of 1 for the first named subtype |
| ``R`` but this does not necessarily apply to ``RS``. When writing |
| portable Ada code, you should avoid writing code that explicitly or |
| implicitly relies on the alignment of such subtypes. |
| |
| For the GNAT compiler, if an explicit alignment clause is given, this |
| value is also used for any subsequent subtypes. So for GNAT, in the |
| above example, you can count on the alignment of ``RS`` being 1. But this |
| assumption is non-portable, and other compilers may choose different |
| alignments for the subtype ``RS``. |
| |
| .. _Size_Clauses: |
| |
| Size Clauses |
| ============ |
| |
| .. index:: Size Clause |
| |
| The default size for a type ``T`` is obtainable through the |
| language-defined attribute ``T'Size`` and also through the |
| equivalent GNAT-defined attribute ``T'Value_Size``. |
| For objects of type ``T``, GNAT will generally increase the type size |
| so that the object size (obtainable through the GNAT-defined attribute |
| ``T'Object_Size``) |
| is a multiple of ``T'Alignment * Storage_Unit``. |
| |
| For example: |
| |
| .. code-block:: ada |
| |
| type Smallint is range 1 .. 6; |
| |
| type Rec is record |
| Y1 : integer; |
| Y2 : boolean; |
| end record; |
| |
| In this example, ``Smallint'Size`` = ``Smallint'Value_Size`` = 3, |
| as specified by the RM rules, |
| but objects of this type will have a size of 8 |
| (``Smallint'Object_Size`` = 8), |
| since objects by default occupy an integral number |
| of storage units. On some targets, notably older |
| versions of the Digital Alpha, the size of stand |
| alone objects of this type may be 32, reflecting |
| the inability of the hardware to do byte load/stores. |
| |
| Similarly, the size of type ``Rec`` is 40 bits |
| (``Rec'Size`` = ``Rec'Value_Size`` = 40), but |
| the alignment is 4, so objects of this type will have |
| their size increased to 64 bits so that it is a multiple |
| of the alignment (in bits). This decision is |
| in accordance with the specific Implementation Advice in RM 13.3(43): |
| |
| "A ``Size`` clause should be supported for an object if the specified |
| ``Size`` is at least as large as its subtype's ``Size``, and corresponds |
| to a size in storage elements that is a multiple of the object's |
| ``Alignment`` (if the ``Alignment`` is nonzero)." |
| |
| An explicit size clause may be used to override the default size by |
| increasing it. For example, if we have: |
| |
| .. code-block:: ada |
| |
| type My_Boolean is new Boolean; |
| for My_Boolean'Size use 32; |
| |
| then values of this type will always be 32-bit long. In the case of discrete |
| types, the size can be increased up to 64 bits on 32-bit targets and 128 bits |
| on 64-bit targets, with the effect that the entire specified field is used to |
| hold the value, sign- or zero-extended as appropriate. If more than 64 bits |
| or 128 bits resp. is specified, then padding space is allocated after the |
| value, and a warning is issued that there are unused bits. |
| |
| Similarly the size of records and arrays may be increased, and the effect |
| is to add padding bits after the value. This also causes a warning message |
| to be generated. |
| |
| The largest Size value permitted in GNAT is 2**31-1. Since this is a |
| Size in bits, this corresponds to an object of size 256 megabytes (minus |
| one). This limitation is true on all targets. The reason for this |
| limitation is that it improves the quality of the code in many cases |
| if it is known that a Size value can be accommodated in an object of |
| type Integer. |
| |
| |
| .. _Storage_Size_Clauses: |
| |
| Storage_Size Clauses |
| ==================== |
| |
| .. index:: Storage_Size Clause |
| |
| For tasks, the ``Storage_Size`` clause specifies the amount of space |
| to be allocated for the task stack. This cannot be extended, and if the |
| stack is exhausted, then ``Storage_Error`` will be raised (if stack |
| checking is enabled). Use a ``Storage_Size`` attribute definition clause, |
| or a ``Storage_Size`` pragma in the task definition to set the |
| appropriate required size. A useful technique is to include in every |
| task definition a pragma of the form: |
| |
| .. code-block:: ada |
| |
| pragma Storage_Size (Default_Stack_Size); |
| |
| Then ``Default_Stack_Size`` can be defined in a global package, and |
| modified as required. Any tasks requiring stack sizes different from the |
| default can have an appropriate alternative reference in the pragma. |
| |
| You can also use the *-d* binder switch to modify the default stack |
| size. |
| |
| For access types, the ``Storage_Size`` clause specifies the maximum |
| space available for allocation of objects of the type. If this space is |
| exceeded then ``Storage_Error`` will be raised by an allocation attempt. |
| In the case where the access type is declared local to a subprogram, the |
| use of a ``Storage_Size`` clause triggers automatic use of a special |
| predefined storage pool (``System.Pool_Size``) that ensures that all |
| space for the pool is automatically reclaimed on exit from the scope in |
| which the type is declared. |
| |
| A special case recognized by the compiler is the specification of a |
| ``Storage_Size`` of zero for an access type. This means that no |
| items can be allocated from the pool, and this is recognized at compile |
| time, and all the overhead normally associated with maintaining a fixed |
| size storage pool is eliminated. Consider the following example: |
| |
| .. code-block:: ada |
| |
| procedure p is |
| type R is array (Natural) of Character; |
| type P is access all R; |
| for P'Storage_Size use 0; |
| -- Above access type intended only for interfacing purposes |
| |
| y : P; |
| |
| procedure g (m : P); |
| pragma Import (C, g); |
| |
| -- ... |
| |
| begin |
| -- ... |
| y := new R; |
| end; |
| |
| As indicated in this example, these dummy storage pools are often useful in |
| connection with interfacing where no object will ever be allocated. If you |
| compile the above example, you get the warning: |
| |
| :: |
| |
| p.adb:16:09: warning: allocation from empty storage pool |
| p.adb:16:09: warning: Storage_Error will be raised at run time |
| |
| |
| Of course in practice, there will not be any explicit allocators in the |
| case of such an access declaration. |
| |
| .. _Size_of_Variant_Record_Objects: |
| |
| Size of Variant Record Objects |
| ============================== |
| |
| .. index:: Size, variant record objects |
| |
| .. index:: Variant record objects, size |
| |
| In the case of variant record objects, there is a question whether Size gives |
| information about a particular variant, or the maximum size required |
| for any variant. Consider the following program |
| |
| .. code-block:: ada |
| |
| with Text_IO; use Text_IO; |
| procedure q is |
| type R1 (A : Boolean := False) is record |
| case A is |
| when True => X : Character; |
| when False => null; |
| end case; |
| end record; |
| |
| V1 : R1 (False); |
| V2 : R1; |
| |
| begin |
| Put_Line (Integer'Image (V1'Size)); |
| Put_Line (Integer'Image (V2'Size)); |
| end q; |
| |
| Here we are dealing with a variant record, where the True variant |
| requires 16 bits, and the False variant requires 8 bits. |
| In the above example, both V1 and V2 contain the False variant, |
| which is only 8 bits long. However, the result of running the |
| program is: |
| |
| :: |
| |
| 8 |
| 16 |
| |
| The reason for the difference here is that the discriminant value of |
| V1 is fixed, and will always be False. It is not possible to assign |
| a True variant value to V1, therefore 8 bits is sufficient. On the |
| other hand, in the case of V2, the initial discriminant value is |
| False (from the default), but it is possible to assign a True |
| variant value to V2, therefore 16 bits must be allocated for V2 |
| in the general case, even fewer bits may be needed at any particular |
| point during the program execution. |
| |
| As can be seen from the output of this program, the ``'Size`` |
| attribute applied to such an object in GNAT gives the actual allocated |
| size of the variable, which is the largest size of any of the variants. |
| The Ada Reference Manual is not completely clear on what choice should |
| be made here, but the GNAT behavior seems most consistent with the |
| language in the RM. |
| |
| In some cases, it may be desirable to obtain the size of the current |
| variant, rather than the size of the largest variant. This can be |
| achieved in GNAT by making use of the fact that in the case of a |
| subprogram parameter, GNAT does indeed return the size of the current |
| variant (because a subprogram has no way of knowing how much space |
| is actually allocated for the actual). |
| |
| Consider the following modified version of the above program: |
| |
| .. code-block:: ada |
| |
| with Text_IO; use Text_IO; |
| procedure q is |
| type R1 (A : Boolean := False) is record |
| case A is |
| when True => X : Character; |
| when False => null; |
| end case; |
| end record; |
| |
| V2 : R1; |
| |
| function Size (V : R1) return Integer is |
| begin |
| return V'Size; |
| end Size; |
| |
| begin |
| Put_Line (Integer'Image (V2'Size)); |
| Put_Line (Integer'Image (Size (V2))); |
| V2 := (True, 'x'); |
| Put_Line (Integer'Image (V2'Size)); |
| Put_Line (Integer'Image (Size (V2))); |
| end q; |
| |
| The output from this program is |
| |
| :: |
| |
| 16 |
| 8 |
| 16 |
| 16 |
| |
| Here we see that while the ``'Size`` attribute always returns |
| the maximum size, regardless of the current variant value, the |
| ``Size`` function does indeed return the size of the current |
| variant value. |
| |
| |
| .. _Biased_Representation: |
| |
| Biased Representation |
| ===================== |
| |
| .. index:: Size for biased representation |
| |
| .. index:: Biased representation |
| |
| In the case of scalars with a range starting at other than zero, it is |
| possible in some cases to specify a size smaller than the default minimum |
| value, and in such cases, GNAT uses an unsigned biased representation, |
| in which zero is used to represent the lower bound, and successive values |
| represent successive values of the type. |
| |
| For example, suppose we have the declaration: |
| |
| .. code-block:: ada |
| |
| type Small is range -7 .. -4; |
| for Small'Size use 2; |
| |
| Although the default size of type ``Small`` is 4, the ``Size`` |
| clause is accepted by GNAT and results in the following representation |
| scheme: |
| |
| :: |
| |
| -7 is represented as 2#00# |
| -6 is represented as 2#01# |
| -5 is represented as 2#10# |
| -4 is represented as 2#11# |
| |
| Biased representation is only used if the specified ``Size`` clause |
| cannot be accepted in any other manner. These reduced sizes that force |
| biased representation can be used for all discrete types except for |
| enumeration types for which a representation clause is given. |
| |
| |
| .. _Value_Size_and_Object_Size_Clauses: |
| |
| Value_Size and Object_Size Clauses |
| ================================== |
| |
| .. index:: Value_Size |
| .. index:: Object_Size |
| .. index:: Size, of objects |
| |
| In Ada 95 and Ada 2005, ``T'Size`` for a type ``T`` is the minimum |
| number of bits required to hold values of type ``T``. |
| Although this interpretation was allowed in Ada 83, it was not required, |
| and this requirement in practice can cause some significant difficulties. |
| For example, in most Ada 83 compilers, ``Natural'Size`` was 32. |
| However, in Ada 95 and Ada 2005, |
| ``Natural'Size`` is |
| typically 31. This means that code may change in behavior when moving |
| from Ada 83 to Ada 95 or Ada 2005. For example, consider: |
| |
| .. code-block:: ada |
| |
| type Rec is record |
| A : Natural; |
| B : Natural; |
| end record; |
| |
| for Rec use record |
| A at 0 range 0 .. Natural'Size - 1; |
| B at 0 range Natural'Size .. 2 * Natural'Size - 1; |
| end record; |
| |
| In the above code, since the typical size of ``Natural`` objects |
| is 32 bits and ``Natural'Size`` is 31, the above code can cause |
| unexpected inefficient packing in Ada 95 and Ada 2005, and in general |
| there are cases where the fact that the object size can exceed the |
| size of the type causes surprises. |
| |
| To help get around this problem GNAT provides two implementation |
| defined attributes, ``Value_Size`` and ``Object_Size``. When |
| applied to a type, these attributes yield the size of the type |
| (corresponding to the RM defined size attribute), and the size of |
| objects of the type respectively. |
| |
| The ``Object_Size`` is used for determining the default size of |
| objects and components. This size value can be referred to using the |
| ``Object_Size`` attribute. The phrase 'is used' here means that it is |
| the basis of the determination of the size. The backend is free to |
| pad this up if necessary for efficiency, e.g., an 8-bit stand-alone |
| character might be stored in 32 bits on a machine with no efficient |
| byte access instructions such as the Alpha. |
| |
| The default rules for the value of ``Object_Size`` for |
| discrete types are as follows: |
| |
| * |
| The ``Object_Size`` for base subtypes reflect the natural hardware |
| size in bits (run the compiler with *-gnatS* to find those values |
| for numeric types). Enumeration types and fixed-point base subtypes have |
| 8, 16, 32, or 64 bits for this size, depending on the range of values |
| to be stored. |
| |
| * |
| The ``Object_Size`` of a subtype is the same as the |
| ``Object_Size`` of |
| the type from which it is obtained. |
| |
| * |
| The ``Object_Size`` of a derived base type is copied from the parent |
| base type, and the ``Object_Size`` of a derived first subtype is copied |
| from the parent first subtype. |
| |
| The ``Value_Size`` attribute |
| is the (minimum) number of bits required to store a value |
| of the type. |
| This value is used to determine how tightly to pack |
| records or arrays with components of this type, and also affects |
| the semantics of unchecked conversion (unchecked conversions where |
| the ``Value_Size`` values differ generate a warning, and are potentially |
| target dependent). |
| |
| The default rules for the value of ``Value_Size`` are as follows: |
| |
| * |
| The ``Value_Size`` for a base subtype is the minimum number of bits |
| required to store all values of the type (including the sign bit |
| only if negative values are possible). |
| |
| * |
| If a subtype statically matches the first subtype of a given type, then it has |
| by default the same ``Value_Size`` as the first subtype. (This is a |
| consequence of RM 13.1(14): "if two subtypes statically match, |
| then their subtype-specific aspects are the same".) |
| |
| * |
| All other subtypes have a ``Value_Size`` corresponding to the minimum |
| number of bits required to store all values of the subtype. For |
| dynamic bounds, it is assumed that the value can range down or up |
| to the corresponding bound of the ancestor |
| |
| The RM defined attribute ``Size`` corresponds to the |
| ``Value_Size`` attribute. |
| |
| The ``Size`` attribute may be defined for a first-named subtype. This sets |
| the ``Value_Size`` of |
| the first-named subtype to the given value, and the |
| ``Object_Size`` of this first-named subtype to the given value padded up |
| to an appropriate boundary. It is a consequence of the default rules |
| above that this ``Object_Size`` will apply to all further subtypes. On the |
| other hand, ``Value_Size`` is affected only for the first subtype, any |
| dynamic subtypes obtained from it directly, and any statically matching |
| subtypes. The ``Value_Size`` of any other static subtypes is not affected. |
| |
| ``Value_Size`` and |
| ``Object_Size`` may be explicitly set for any subtype using |
| an attribute definition clause. Note that the use of these attributes |
| can cause the RM 13.1(14) rule to be violated. If two access types |
| reference aliased objects whose subtypes have differing ``Object_Size`` |
| values as a result of explicit attribute definition clauses, then it |
| is illegal to convert from one access subtype to the other. For a more |
| complete description of this additional legality rule, see the |
| description of the ``Object_Size`` attribute. |
| |
| To get a feel for the difference, consider the following examples (note |
| that in each case the base is ``Short_Short_Integer`` with a size of 8): |
| |
| +---------------------------------------------+-------------+-------------+ |
| |Type or subtype declaration | Object_Size | Value_Size| |
| +=============================================+=============+=============+ |
| |``type x1 is range 0 .. 5;`` | 8 | 3 | |
| +---------------------------------------------+-------------+-------------+ |
| |``type x2 is range 0 .. 5;`` | 16 | 12 | |
| |``for x2'size use 12;`` | | | |
| +---------------------------------------------+-------------+-------------+ |
| |``subtype x3 is x2 range 0 .. 3;`` | 16 | 2 | |
| +---------------------------------------------+-------------+-------------+ |
| |``subtype x4 is x2'base range 0 .. 10;`` | 8 | 4 | |
| +---------------------------------------------+-------------+-------------+ |
| |``dynamic : x2'Base range -64 .. +63;`` | | | |
| +---------------------------------------------+-------------+-------------+ |
| |``subtype x5 is x2 range 0 .. dynamic;`` | 16 | 3* | |
| +---------------------------------------------+-------------+-------------+ |
| |``subtype x6 is x2'base range 0 .. dynamic;``| 8 | 7* | |
| +---------------------------------------------+-------------+-------------+ |
| |
| Note: the entries marked '*' are not actually specified by the Ada |
| Reference Manual, which has nothing to say about size in the dynamic |
| case. What GNAT does is to allocate sufficient bits to accommodate any |
| possible dynamic values for the bounds at run-time. |
| |
| So far, so good, but GNAT has to obey the RM rules, so the question is |
| under what conditions must the RM ``Size`` be used. |
| The following is a list |
| of the occasions on which the RM ``Size`` must be used: |
| |
| * |
| Component size for packed arrays or records |
| |
| * |
| Value of the attribute ``Size`` for a type |
| |
| * |
| Warning about sizes not matching for unchecked conversion |
| |
| For record types, the ``Object_Size`` is always a multiple of the |
| alignment of the type (this is true for all types). In some cases the |
| ``Value_Size`` can be smaller. Consider: |
| |
| |
| .. code-block:: ada |
| |
| type R is record |
| X : Integer; |
| Y : Character; |
| end record; |
| |
| |
| On a typical 32-bit architecture, the X component will occupy four bytes |
| and the Y component will occupy one byte, for a total of 5 bytes. As a |
| result ``R'Value_Size`` will be 40 (bits) since this is the minimum size |
| required to store a value of this type. For example, it is permissible |
| to have a component of type R in an array whose component size is |
| specified to be 40 bits. |
| |
| However, ``R'Object_Size`` will be 64 (bits). The difference is due to |
| the alignment requirement for objects of the record type. The X |
| component will require four-byte alignment because that is what type |
| Integer requires, whereas the Y component, a Character, will only |
| require 1-byte alignment. Since the alignment required for X is the |
| greatest of all the components' alignments, that is the alignment |
| required for the enclosing record type, i.e., 4 bytes or 32 bits. As |
| indicated above, the actual object size must be rounded up so that it is |
| a multiple of the alignment value. Therefore, 40 bits rounded up to the |
| next multiple of 32 yields 64 bits. |
| |
| For all other types, the ``Object_Size`` |
| and ``Value_Size`` are the same (and equivalent to the RM attribute ``Size``). |
| Only ``Size`` may be specified for such types. |
| |
| Note that ``Value_Size`` can be used to force biased representation |
| for a particular subtype. Consider this example: |
| |
| |
| .. code-block:: ada |
| |
| type R is (A, B, C, D, E, F); |
| subtype RAB is R range A .. B; |
| subtype REF is R range E .. F; |
| |
| |
| By default, ``RAB`` |
| has a size of 1 (sufficient to accommodate the representation |
| of ``A`` and ``B``, 0 and 1), and ``REF`` |
| has a size of 3 (sufficient to accommodate the representation |
| of ``E`` and ``F``, 4 and 5). But if we add the |
| following ``Value_Size`` attribute definition clause: |
| |
| |
| .. code-block:: ada |
| |
| for REF'Value_Size use 1; |
| |
| |
| then biased representation is forced for ``REF``, |
| and 0 will represent ``E`` and 1 will represent ``F``. |
| A warning is issued when a ``Value_Size`` attribute |
| definition clause forces biased representation. This |
| warning can be turned off using :switch:`-gnatw.B`. |
| |
| .. _Component_Size_Clauses: |
| |
| Component_Size Clauses |
| ====================== |
| |
| .. index:: Component_Size Clause |
| |
| Normally, the value specified in a component size clause must be consistent |
| with the subtype of the array component with regard to size and alignment. |
| In other words, the value specified must be at least equal to the size |
| of this subtype, and must be a multiple of the alignment value. |
| |
| In addition, component size clauses are allowed which cause the array |
| to be packed, by specifying a smaller value. A first case is for |
| component size values in the range 1 through 63 on 32-bit targets, |
| and 1 through 127 on 64-bit targets. The value specified may not |
| be smaller than the Size of the subtype. GNAT will accurately |
| honor all packing requests in this range. For example, if we have: |
| |
| |
| .. code-block:: ada |
| |
| type r is array (1 .. 8) of Natural; |
| for r'Component_Size use 31; |
| |
| |
| then the resulting array has a length of 31 bytes (248 bits = 8 * 31). |
| Of course access to the components of such an array is considerably |
| less efficient than if the natural component size of 32 is used. |
| A second case is when the subtype of the component is a record type |
| padded because of its default alignment. For example, if we have: |
| |
| |
| .. code-block:: ada |
| |
| type r is record |
| i : Integer; |
| j : Integer; |
| b : Boolean; |
| end record; |
| |
| type a is array (1 .. 8) of r; |
| for a'Component_Size use 72; |
| |
| |
| then the resulting array has a length of 72 bytes, instead of 96 bytes |
| if the alignment of the record (4) was obeyed. |
| |
| Note that there is no point in giving both a component size clause |
| and a pragma Pack for the same array type. if such duplicate |
| clauses are given, the pragma Pack will be ignored. |
| |
| .. _Bit_Order_Clauses: |
| |
| Bit_Order Clauses |
| ================= |
| |
| .. index:: Bit_Order Clause |
| |
| .. index:: bit ordering |
| |
| .. index:: ordering, of bits |
| |
| For record subtypes, GNAT permits the specification of the ``Bit_Order`` |
| attribute. The specification may either correspond to the default bit |
| order for the target, in which case the specification has no effect and |
| places no additional restrictions, or it may be for the non-standard |
| setting (that is the opposite of the default). |
| |
| In the case where the non-standard value is specified, the effect is |
| to renumber bits within each byte, but the ordering of bytes is not |
| affected. There are certain |
| restrictions placed on component clauses as follows: |
| |
| |
| * Components fitting within a single storage unit. |
| |
| These are unrestricted, and the effect is merely to renumber bits. For |
| example if we are on a little-endian machine with ``Low_Order_First`` |
| being the default, then the following two declarations have exactly |
| the same effect: |
| |
| |
| :: |
| |
| type R1 is record |
| A : Boolean; |
| B : Integer range 1 .. 120; |
| end record; |
| |
| for R1 use record |
| A at 0 range 0 .. 0; |
| B at 0 range 1 .. 7; |
| end record; |
| |
| type R2 is record |
| A : Boolean; |
| B : Integer range 1 .. 120; |
| end record; |
| |
| for R2'Bit_Order use High_Order_First; |
| |
| for R2 use record |
| A at 0 range 7 .. 7; |
| B at 0 range 0 .. 6; |
| end record; |
| |
| |
| The useful application here is to write the second declaration with the |
| ``Bit_Order`` attribute definition clause, and know that it will be treated |
| the same, regardless of whether the target is little-endian or big-endian. |
| |
| * Components occupying an integral number of bytes. |
| |
| These are components that exactly fit in two or more bytes. Such component |
| declarations are allowed, but have no effect, since it is important to realize |
| that the ``Bit_Order`` specification does not affect the ordering of bytes. |
| In particular, the following attempt at getting an endian-independent integer |
| does not work: |
| |
| |
| :: |
| |
| type R2 is record |
| A : Integer; |
| end record; |
| |
| for R2'Bit_Order use High_Order_First; |
| |
| for R2 use record |
| A at 0 range 0 .. 31; |
| end record; |
| |
| |
| This declaration will result in a little-endian integer on a |
| little-endian machine, and a big-endian integer on a big-endian machine. |
| If byte flipping is required for interoperability between big- and |
| little-endian machines, this must be explicitly programmed. This capability |
| is not provided by ``Bit_Order``. |
| |
| * Components that are positioned across byte boundaries. |
| |
| but do not occupy an integral number of bytes. Given that bytes are not |
| reordered, such fields would occupy a non-contiguous sequence of bits |
| in memory, requiring non-trivial code to reassemble. They are for this |
| reason not permitted, and any component clause specifying such a layout |
| will be flagged as illegal by GNAT. |
| |
| |
| Since the misconception that Bit_Order automatically deals with all |
| endian-related incompatibilities is a common one, the specification of |
| a component field that is an integral number of bytes will always |
| generate a warning. This warning may be suppressed using ``pragma Warnings (Off)`` |
| if desired. The following section contains additional |
| details regarding the issue of byte ordering. |
| |
| .. _Effect_of_Bit_Order_on_Byte_Ordering: |
| |
| Effect of Bit_Order on Byte Ordering |
| ==================================== |
| |
| .. index:: byte ordering |
| |
| .. index:: ordering, of bytes |
| |
| In this section we will review the effect of the ``Bit_Order`` attribute |
| definition clause on byte ordering. Briefly, it has no effect at all, but |
| a detailed example will be helpful. Before giving this |
| example, let us review the precise |
| definition of the effect of defining ``Bit_Order``. The effect of a |
| non-standard bit order is described in section 13.5.3 of the Ada |
| Reference Manual: |
| |
| "2 A bit ordering is a method of interpreting the meaning of |
| the storage place attributes." |
| |
| To understand the precise definition of storage place attributes in |
| this context, we visit section 13.5.1 of the manual: |
| |
| "13 A record_representation_clause (without the mod_clause) |
| specifies the layout. The storage place attributes (see 13.5.2) |
| are taken from the values of the position, first_bit, and last_bit |
| expressions after normalizing those values so that first_bit is |
| less than Storage_Unit." |
| |
| The critical point here is that storage places are taken from |
| the values after normalization, not before. So the ``Bit_Order`` |
| interpretation applies to normalized values. The interpretation |
| is described in the later part of the 13.5.3 paragraph: |
| |
| "2 A bit ordering is a method of interpreting the meaning of |
| the storage place attributes. High_Order_First (known in the |
| vernacular as 'big endian') means that the first bit of a |
| storage element (bit 0) is the most significant bit (interpreting |
| the sequence of bits that represent a component as an unsigned |
| integer value). Low_Order_First (known in the vernacular as |
| 'little endian') means the opposite: the first bit is the |
| least significant." |
| |
| Note that the numbering is with respect to the bits of a storage |
| unit. In other words, the specification affects only the numbering |
| of bits within a single storage unit. |
| |
| We can make the effect clearer by giving an example. |
| |
| Suppose that we have an external device which presents two bytes, the first |
| byte presented, which is the first (low addressed byte) of the two byte |
| record is called Master, and the second byte is called Slave. |
| |
| The left most (most significant) bit is called Control for each byte, and |
| the remaining 7 bits are called V1, V2, ... V7, where V7 is the rightmost |
| (least significant) bit. |
| |
| On a big-endian machine, we can write the following representation clause |
| |
| |
| .. code-block:: ada |
| |
| type Data is record |
| Master_Control : Bit; |
| Master_V1 : Bit; |
| Master_V2 : Bit; |
| Master_V3 : Bit; |
| Master_V4 : Bit; |
| Master_V5 : Bit; |
| Master_V6 : Bit; |
| Master_V7 : Bit; |
| Slave_Control : Bit; |
| Slave_V1 : Bit; |
| Slave_V2 : Bit; |
| Slave_V3 : Bit; |
| Slave_V4 : Bit; |
| Slave_V5 : Bit; |
| Slave_V6 : Bit; |
| Slave_V7 : Bit; |
| end record; |
| |
| for Data use record |
| Master_Control at 0 range 0 .. 0; |
| Master_V1 at 0 range 1 .. 1; |
| Master_V2 at 0 range 2 .. 2; |
| Master_V3 at 0 range 3 .. 3; |
| Master_V4 at 0 range 4 .. 4; |
| Master_V5 at 0 range 5 .. 5; |
| Master_V6 at 0 range 6 .. 6; |
| Master_V7 at 0 range 7 .. 7; |
| Slave_Control at 1 range 0 .. 0; |
| Slave_V1 at 1 range 1 .. 1; |
| Slave_V2 at 1 range 2 .. 2; |
| Slave_V3 at 1 range 3 .. 3; |
| Slave_V4 at 1 range 4 .. 4; |
| Slave_V5 at 1 range 5 .. 5; |
| Slave_V6 at 1 range 6 .. 6; |
| Slave_V7 at 1 range 7 .. 7; |
| end record; |
| |
| |
| Now if we move this to a little endian machine, then the bit ordering within |
| the byte is backwards, so we have to rewrite the record rep clause as: |
| |
| |
| .. code-block:: ada |
| |
| for Data use record |
| Master_Control at 0 range 7 .. 7; |
| Master_V1 at 0 range 6 .. 6; |
| Master_V2 at 0 range 5 .. 5; |
| Master_V3 at 0 range 4 .. 4; |
| Master_V4 at 0 range 3 .. 3; |
| Master_V5 at 0 range 2 .. 2; |
| Master_V6 at 0 range 1 .. 1; |
| Master_V7 at 0 range 0 .. 0; |
| Slave_Control at 1 range 7 .. 7; |
| Slave_V1 at 1 range 6 .. 6; |
| Slave_V2 at 1 range 5 .. 5; |
| Slave_V3 at 1 range 4 .. 4; |
| Slave_V4 at 1 range 3 .. 3; |
| Slave_V5 at 1 range 2 .. 2; |
| Slave_V6 at 1 range 1 .. 1; |
| Slave_V7 at 1 range 0 .. 0; |
| end record; |
| |
| |
| It is a nuisance to have to rewrite the clause, especially if |
| the code has to be maintained on both machines. However, |
| this is a case that we can handle with the |
| ``Bit_Order`` attribute if it is implemented. |
| Note that the implementation is not required on byte addressed |
| machines, but it is indeed implemented in GNAT. |
| This means that we can simply use the |
| first record clause, together with the declaration |
| |
| |
| .. code-block:: ada |
| |
| for Data'Bit_Order use High_Order_First; |
| |
| |
| and the effect is what is desired, namely the layout is exactly the same, |
| independent of whether the code is compiled on a big-endian or little-endian |
| machine. |
| |
| The important point to understand is that byte ordering is not affected. |
| A ``Bit_Order`` attribute definition never affects which byte a field |
| ends up in, only where it ends up in that byte. |
| To make this clear, let us rewrite the record rep clause of the previous |
| example as: |
| |
| |
| .. code-block:: ada |
| |
| for Data'Bit_Order use High_Order_First; |
| for Data use record |
| Master_Control at 0 range 0 .. 0; |
| Master_V1 at 0 range 1 .. 1; |
| Master_V2 at 0 range 2 .. 2; |
| Master_V3 at 0 range 3 .. 3; |
| Master_V4 at 0 range 4 .. 4; |
| Master_V5 at 0 range 5 .. 5; |
| Master_V6 at 0 range 6 .. 6; |
| Master_V7 at 0 range 7 .. 7; |
| Slave_Control at 0 range 8 .. 8; |
| Slave_V1 at 0 range 9 .. 9; |
| Slave_V2 at 0 range 10 .. 10; |
| Slave_V3 at 0 range 11 .. 11; |
| Slave_V4 at 0 range 12 .. 12; |
| Slave_V5 at 0 range 13 .. 13; |
| Slave_V6 at 0 range 14 .. 14; |
| Slave_V7 at 0 range 15 .. 15; |
| end record; |
| |
| |
| This is exactly equivalent to saying (a repeat of the first example): |
| |
| |
| .. code-block:: ada |
| |
| for Data'Bit_Order use High_Order_First; |
| for Data use record |
| Master_Control at 0 range 0 .. 0; |
| Master_V1 at 0 range 1 .. 1; |
| Master_V2 at 0 range 2 .. 2; |
| Master_V3 at 0 range 3 .. 3; |
| Master_V4 at 0 range 4 .. 4; |
| Master_V5 at 0 range 5 .. 5; |
| Master_V6 at 0 range 6 .. 6; |
| Master_V7 at 0 range 7 .. 7; |
| Slave_Control at 1 range 0 .. 0; |
| Slave_V1 at 1 range 1 .. 1; |
| Slave_V2 at 1 range 2 .. 2; |
| Slave_V3 at 1 range 3 .. 3; |
| Slave_V4 at 1 range 4 .. 4; |
| Slave_V5 at 1 range 5 .. 5; |
| Slave_V6 at 1 range 6 .. 6; |
| Slave_V7 at 1 range 7 .. 7; |
| end record; |
| |
| |
| Why are they equivalent? Well take a specific field, the ``Slave_V2`` |
| field. The storage place attributes are obtained by normalizing the |
| values given so that the ``First_Bit`` value is less than 8. After |
| normalizing the values (0,10,10) we get (1,2,2) which is exactly what |
| we specified in the other case. |
| |
| Now one might expect that the ``Bit_Order`` attribute might affect |
| bit numbering within the entire record component (two bytes in this |
| case, thus affecting which byte fields end up in), but that is not |
| the way this feature is defined, it only affects numbering of bits, |
| not which byte they end up in. |
| |
| Consequently it never makes sense to specify a starting bit number |
| greater than 7 (for a byte addressable field) if an attribute |
| definition for ``Bit_Order`` has been given, and indeed it |
| may be actively confusing to specify such a value, so the compiler |
| generates a warning for such usage. |
| |
| If you do need to control byte ordering then appropriate conditional |
| values must be used. If in our example, the slave byte came first on |
| some machines we might write: |
| |
| .. code-block:: ada |
| |
| Master_Byte_First constant Boolean := ...; |
| |
| Master_Byte : constant Natural := |
| 1 - Boolean'Pos (Master_Byte_First); |
| Slave_Byte : constant Natural := |
| Boolean'Pos (Master_Byte_First); |
| |
| for Data'Bit_Order use High_Order_First; |
| for Data use record |
| Master_Control at Master_Byte range 0 .. 0; |
| Master_V1 at Master_Byte range 1 .. 1; |
| Master_V2 at Master_Byte range 2 .. 2; |
| Master_V3 at Master_Byte range 3 .. 3; |
| Master_V4 at Master_Byte range 4 .. 4; |
| Master_V5 at Master_Byte range 5 .. 5; |
| Master_V6 at Master_Byte range 6 .. 6; |
| Master_V7 at Master_Byte range 7 .. 7; |
| Slave_Control at Slave_Byte range 0 .. 0; |
| Slave_V1 at Slave_Byte range 1 .. 1; |
| Slave_V2 at Slave_Byte range 2 .. 2; |
| Slave_V3 at Slave_Byte range 3 .. 3; |
| Slave_V4 at Slave_Byte range 4 .. 4; |
| Slave_V5 at Slave_Byte range 5 .. 5; |
| Slave_V6 at Slave_Byte range 6 .. 6; |
| Slave_V7 at Slave_Byte range 7 .. 7; |
| end record; |
| |
| Now to switch between machines, all that is necessary is |
| to set the boolean constant ``Master_Byte_First`` in |
| an appropriate manner. |
| |
| .. _Pragma_Pack_for_Arrays: |
| |
| Pragma Pack for Arrays |
| ====================== |
| |
| .. index:: Pragma Pack (for arrays) |
| |
| Pragma ``Pack`` applied to an array has an effect that depends upon whether the |
| component type is *packable*. For a component type to be *packable*, it must |
| be one of the following cases: |
| |
| * Any elementary type. |
| |
| * Any small packed array type with a static size. |
| |
| * Any small simple record type with a static size. |
| |
| For all these cases, if the component subtype size is in the range |
| 1 through 63 on 32-bit targets, and 1 through 127 on 64-bit targets, |
| then the effect of the pragma ``Pack`` is exactly as though a |
| component size were specified giving the component subtype size. |
| |
| All other types are non-packable, they occupy an integral number of storage |
| units and the only effect of pragma Pack is to remove alignment gaps. |
| |
| For example if we have: |
| |
| .. code-block:: ada |
| |
| type r is range 0 .. 17; |
| |
| type ar is array (1 .. 8) of r; |
| pragma Pack (ar); |
| |
| Then the component size of ``ar`` will be set to 5 (i.e., to ``r'size``, |
| and the size of the array ``ar`` will be exactly 40 bits). |
| |
| Note that in some cases this rather fierce approach to packing can produce |
| unexpected effects. For example, in Ada 95 and Ada 2005, |
| subtype ``Natural`` typically has a size of 31, meaning that if you |
| pack an array of ``Natural``, you get 31-bit |
| close packing, which saves a few bits, but results in far less efficient |
| access. Since many other Ada compilers will ignore such a packing request, |
| GNAT will generate a warning on some uses of pragma ``Pack`` that it guesses |
| might not be what is intended. You can easily remove this warning by |
| using an explicit ``Component_Size`` setting instead, which never generates |
| a warning, since the intention of the programmer is clear in this case. |
| |
| GNAT treats packed arrays in one of two ways. If the size of the array is |
| known at compile time and is at most 64 bits on 32-bit targets, and at most |
| 128 bits on 64-bit targets, then internally the array is represented as a |
| single modular type, of exactly the appropriate number of bits. If the |
| length is greater than 64 bits on 32-bit targets, and greater than 128 |
| bits on 64-bit targets, or is not known at compile time, then the packed |
| array is represented as an array of bytes, and its length is always a |
| multiple of 8 bits. |
| |
| Note that to represent a packed array as a modular type, the alignment must |
| be suitable for the modular type involved. For example, on typical machines |
| a 32-bit packed array will be represented by a 32-bit modular integer with |
| an alignment of four bytes. If you explicitly override the default alignment |
| with an alignment clause that is too small, the modular representation |
| cannot be used. For example, consider the following set of declarations: |
| |
| .. code-block:: ada |
| |
| type R is range 1 .. 3; |
| type S is array (1 .. 31) of R; |
| for S'Component_Size use 2; |
| for S'Size use 62; |
| for S'Alignment use 1; |
| |
| If the alignment clause were not present, then a 62-bit modular |
| representation would be chosen (typically with an alignment of 4 or 8 |
| bytes depending on the target). But the default alignment is overridden |
| with the explicit alignment clause. This means that the modular |
| representation cannot be used, and instead the array of bytes |
| representation must be used, meaning that the length must be a multiple |
| of 8. Thus the above set of declarations will result in a diagnostic |
| rejecting the size clause and noting that the minimum size allowed is 64. |
| |
| .. index:: Pragma Pack (for type Natural) |
| |
| .. index:: Pragma Pack warning |
| |
| One special case that is worth noting occurs when the base type of the |
| component size is 8/16/32 and the subtype is one bit less. Notably this |
| occurs with subtype ``Natural``. Consider: |
| |
| .. code-block:: ada |
| |
| type Arr is array (1 .. 32) of Natural; |
| pragma Pack (Arr); |
| |
| In all commonly used Ada 83 compilers, this pragma Pack would be ignored, |
| since typically ``Natural'Size`` is 32 in Ada 83, and in any case most |
| Ada 83 compilers did not attempt 31 bit packing. |
| |
| In Ada 95 and Ada 2005, ``Natural'Size`` is required to be 31. Furthermore, |
| GNAT really does pack 31-bit subtype to 31 bits. This may result in a |
| substantial unintended performance penalty when porting legacy Ada 83 code. |
| To help prevent this, GNAT generates a warning in such cases. If you really |
| want 31 bit packing in a case like this, you can set the component size |
| explicitly: |
| |
| .. code-block:: ada |
| |
| type Arr is array (1 .. 32) of Natural; |
| for Arr'Component_Size use 31; |
| |
| Here 31-bit packing is achieved as required, and no warning is generated, |
| since in this case the programmer intention is clear. |
| |
| .. _Pragma_Pack_for_Records: |
| |
| Pragma Pack for Records |
| ======================= |
| |
| .. index:: Pragma Pack (for records) |
| |
| Pragma ``Pack`` applied to a record will pack the components to reduce |
| wasted space from alignment gaps and by reducing the amount of space |
| taken by components. We distinguish between *packable* components and |
| *non-packable* components. |
| Components of the following types are considered packable: |
| |
| * Components of an elementary type are packable unless they are aliased, |
| independent or atomic. |
| |
| * Small packed arrays, where the size is statically known, are represented |
| internally as modular integers, and so they are also packable. |
| |
| * Small simple records, where the size is statically known, are also packable. |
| |
| For all these cases, if the ``'Size`` value is in the range 1 through 64 on |
| 32-bit targets, and 1 through 128 on 64-bit targets, the components occupy |
| the exact number of bits corresponding to this value and are packed with no |
| padding bits, i.e. they can start on an arbitrary bit boundary. |
| |
| All other types are non-packable, they occupy an integral number of storage |
| units and the only effect of pragma ``Pack`` is to remove alignment gaps. |
| |
| For example, consider the record |
| |
| .. code-block:: ada |
| |
| type Rb1 is array (1 .. 13) of Boolean; |
| pragma Pack (Rb1); |
| |
| type Rb2 is array (1 .. 65) of Boolean; |
| pragma Pack (Rb2); |
| |
| type AF is new Float with Atomic; |
| |
| type X2 is record |
| L1 : Boolean; |
| L2 : Duration; |
| L3 : AF; |
| L4 : Boolean; |
| L5 : Rb1; |
| L6 : Rb2; |
| end record; |
| pragma Pack (X2); |
| |
| The representation for the record ``X2`` is as follows on 32-bit targets: |
| |
| .. code-block:: ada |
| |
| for X2'Size use 224; |
| for X2 use record |
| L1 at 0 range 0 .. 0; |
| L2 at 0 range 1 .. 64; |
| L3 at 12 range 0 .. 31; |
| L4 at 16 range 0 .. 0; |
| L5 at 16 range 1 .. 13; |
| L6 at 18 range 0 .. 71; |
| end record; |
| |
| Studying this example, we see that the packable fields ``L1`` |
| and ``L2`` are of length equal to their sizes, and placed at |
| specific bit boundaries (and not byte boundaries) to eliminate |
| padding. But ``L3`` is of a non-packable float type (because |
| it is aliased), so it is on the next appropriate alignment boundary. |
| |
| The next two fields are fully packable, so ``L4`` and ``L5`` are |
| minimally packed with no gaps. However, type ``Rb2`` is a packed |
| array that is longer than 64 bits, so it is itself non-packable on |
| 32-bit targets. Thus the ``L6`` field is aligned to the next byte |
| boundary, and takes an integral number of bytes, i.e., 72 bits. |
| |
| .. _Record_Representation_Clauses: |
| |
| Record Representation Clauses |
| ============================= |
| |
| .. index:: Record Representation Clause |
| |
| Record representation clauses may be given for all record types, including |
| types obtained by record extension. Component clauses are allowed for any |
| static component. The restrictions on component clauses depend on the type |
| of the component. |
| |
| .. index:: Component Clause |
| |
| For all components of an elementary type, the only restriction on component |
| clauses is that the size must be at least the ``'Size`` value of the type |
| (actually the Value_Size). There are no restrictions due to alignment, |
| and such components may freely cross storage boundaries. |
| |
| Packed arrays with a size up to and including 64 bits on 32-bit targets, |
| and up to and including 128 bits on 64-bit targets, are represented |
| internally using a modular type with the appropriate number of bits, and |
| thus the same lack of restriction applies. For example, if you declare: |
| |
| .. code-block:: ada |
| |
| type R is array (1 .. 49) of Boolean; |
| pragma Pack (R); |
| for R'Size use 49; |
| |
| then a component clause for a component of type ``R`` may start on any |
| specified bit boundary, and may specify a value of 49 bits or greater. |
| |
| For packed bit arrays that are longer than 64 bits on 32-bit targets, |
| and longer than 128 bits on 64-bit targets, there are two cases. If the |
| component size is a power of 2 (1,2,4,8,16,32,64 bits), including the |
| important case of single bits or boolean values, then there are no |
| limitations on placement of such components, and they may start and |
| end at arbitrary bit boundaries. |
| |
| If the component size is not a power of 2 (e.g., 3 or 5), then an array |
| of this type must always be placed on on a storage unit (byte) boundary |
| and occupy an integral number of storage units (bytes). Any component |
| clause that does not meet this requirement will be rejected. |
| |
| Any aliased component, or component of an aliased type, must have its |
| normal alignment and size. A component clause that does not meet this |
| requirement will be rejected. |
| |
| The tag field of a tagged type always occupies an address sized field at |
| the start of the record. No component clause may attempt to overlay this |
| tag. When a tagged type appears as a component, the tag field must have |
| proper alignment |
| |
| In the case of a record extension ``T1``, of a type ``T``, no component |
| clause applied to the type ``T1`` can specify a storage location that |
| would overlap the first ``T'Object_Size`` bits of the record. |
| |
| For all other component types, including non-bit-packed arrays, |
| the component can be placed at an arbitrary bit boundary, |
| so for example, the following is permitted: |
| |
| .. code-block:: ada |
| |
| type R is array (1 .. 10) of Boolean; |
| for R'Size use 80; |
| |
| type Q is record |
| G, H : Boolean; |
| L, M : R; |
| end record; |
| |
| for Q use record |
| G at 0 range 0 .. 0; |
| H at 0 range 1 .. 1; |
| L at 0 range 2 .. 81; |
| R at 0 range 82 .. 161; |
| end record; |
| |
| .. _Handling_of_Records_with_Holes: |
| |
| Handling of Records with Holes |
| ============================== |
| |
| .. index:: Handling of Records with Holes |
| |
| As a result of alignment considerations, records may contain "holes" |
| or gaps which do not correspond to the data bits of any of the components. |
| Record representation clauses can also result in holes in records. |
| |
| GNAT does not attempt to clear these holes, so in record objects, |
| they should be considered to hold undefined rubbish. The generated |
| equality routine just tests components so does not access these |
| undefined bits, and assignment and copy operations may or may not |
| preserve the contents of these holes (for assignments, the holes |
| in the target will in practice contain either the bits that are |
| present in the holes in the source, or the bits that were present |
| in the target before the assignment). |
| |
| If it is necessary to ensure that holes in records have all zero |
| bits, then record objects for which this initialization is desired |
| should be explicitly set to all zero values using Unchecked_Conversion |
| or address overlays. For example |
| |
| .. code-block:: ada |
| |
| type HRec is record |
| C : Character; |
| I : Integer; |
| end record; |
| |
| On typical machines, integers need to be aligned on a four-byte |
| boundary, resulting in three bytes of undefined rubbish following |
| the 8-bit field for C. To ensure that the hole in a variable of |
| type HRec is set to all zero bits, |
| you could for example do: |
| |
| .. code-block:: ada |
| |
| type Base is record |
| Dummy1, Dummy2 : Integer := 0; |
| end record; |
| |
| BaseVar : Base; |
| RealVar : Hrec; |
| for RealVar'Address use BaseVar'Address; |
| |
| |
| Now the 8-bytes of the value of RealVar start out containing all zero |
| bits. A safer approach is to just define dummy fields, avoiding the |
| holes, as in: |
| |
| .. code-block:: ada |
| |
| type HRec is record |
| C : Character; |
| Dummy1 : Short_Short_Integer := 0; |
| Dummy2 : Short_Short_Integer := 0; |
| Dummy3 : Short_Short_Integer := 0; |
| I : Integer; |
| end record; |
| |
| And to make absolutely sure that the intent of this is followed, you |
| can use representation clauses: |
| |
| .. code-block:: ada |
| |
| for Hrec use record |
| C at 0 range 0 .. 7; |
| Dummy1 at 1 range 0 .. 7; |
| Dummy2 at 2 range 0 .. 7; |
| Dummy3 at 3 range 0 .. 7; |
| I at 4 range 0 .. 31; |
| end record; |
| for Hrec'Size use 64; |
| |
| |
| .. _Enumeration_Clauses: |
| |
| Enumeration Clauses |
| =================== |
| |
| The only restriction on enumeration clauses is that the range of values |
| must be representable. For the signed case, if one or more of the |
| representation values are negative, all values must be in the range: |
| |
| .. code-block:: ada |
| |
| System.Min_Int .. System.Max_Int |
| |
| For the unsigned case, where all values are nonnegative, the values must |
| be in the range: |
| |
| .. code-block:: ada |
| |
| 0 .. System.Max_Binary_Modulus; |
| |
| |
| A *confirming* representation clause is one in which the values range |
| from 0 in sequence, i.e., a clause that confirms the default representation |
| for an enumeration type. |
| Such a confirming representation |
| is permitted by these rules, and is specially recognized by the compiler so |
| that no extra overhead results from the use of such a clause. |
| |
| If an array has an index type which is an enumeration type to which an |
| enumeration clause has been applied, then the array is stored in a compact |
| manner. Consider the declarations: |
| |
| .. code-block:: ada |
| |
| type r is (A, B, C); |
| for r use (A => 1, B => 5, C => 10); |
| type t is array (r) of Character; |
| |
| The array type t corresponds to a vector with exactly three elements and |
| has a default size equal to ``3*Character'Size``. This ensures efficient |
| use of space, but means that accesses to elements of the array will incur |
| the overhead of converting representation values to the corresponding |
| positional values, (i.e., the value delivered by the ``Pos`` attribute). |
| |
| |
| .. _Address_Clauses: |
| |
| Address Clauses |
| =============== |
| .. index:: Address Clause |
| |
| The reference manual allows a general restriction on representation clauses, |
| as found in RM 13.1(22): |
| |
| "An implementation need not support representation |
| items containing nonstatic expressions, except that |
| an implementation should support a representation item |
| for a given entity if each nonstatic expression in the |
| representation item is a name that statically denotes |
| a constant declared before the entity." |
| |
| In practice this is applicable only to address clauses, since this is the |
| only case in which a nonstatic expression is permitted by the syntax. As |
| the AARM notes in sections 13.1 (22.a-22.h): |
| |
| 22.a Reason: This is to avoid the following sort of thing: |
| |
| 22.b X : Integer := F(...); |
| Y : Address := G(...); |
| for X'Address use Y; |
| |
| 22.c In the above, we have to evaluate the |
| initialization expression for X before we |
| know where to put the result. This seems |
| like an unreasonable implementation burden. |
| |
| 22.d The above code should instead be written |
| like this: |
| |
| 22.e Y : constant Address := G(...); |
| X : Integer := F(...); |
| for X'Address use Y; |
| |
| 22.f This allows the expression 'Y' to be safely |
| evaluated before X is created. |
| |
| 22.g The constant could be a formal parameter of mode in. |
| |
| 22.h An implementation can support other nonstatic |
| expressions if it wants to. Expressions of type |
| Address are hardly ever static, but their value |
| might be known at compile time anyway in many |
| cases. |
| |
| GNAT does indeed permit many additional cases of nonstatic expressions. In |
| particular, if the type involved is elementary there are no restrictions |
| (since in this case, holding a temporary copy of the initialization value, |
| if one is present, is inexpensive). In addition, if there is no implicit or |
| explicit initialization, then there are no restrictions. GNAT will reject |
| only the case where all three of these conditions hold: |
| |
| * |
| The type of the item is non-elementary (e.g., a record or array). |
| |
| * |
| There is explicit or implicit initialization required for the object. |
| Note that access values are always implicitly initialized. |
| |
| * |
| The address value is nonstatic. Here GNAT is more permissive than the |
| RM, and allows the address value to be the address of a previously declared |
| stand-alone variable, as long as it does not itself have an address clause. |
| |
| :: |
| |
| Anchor : Some_Initialized_Type; |
| Overlay : Some_Initialized_Type; |
| for Overlay'Address use Anchor'Address; |
| |
| However, the prefix of the address clause cannot be an array component, or |
| a component of a discriminated record. |
| |
| As noted above in section 22.h, address values are typically nonstatic. In |
| particular the To_Address function, even if applied to a literal value, is |
| a nonstatic function call. To avoid this minor annoyance, GNAT provides |
| the implementation defined attribute 'To_Address. The following two |
| expressions have identical values: |
| |
| .. index:: Attribute |
| .. index:: To_Address |
| |
| .. code-block:: ada |
| |
| To_Address (16#1234_0000#) |
| System'To_Address (16#1234_0000#); |
| |
| except that the second form is considered to be a static expression, and |
| thus when used as an address clause value is always permitted. |
| |
| Additionally, GNAT treats as static an address clause that is an |
| unchecked_conversion of a static integer value. This simplifies the porting |
| of legacy code, and provides a portable equivalent to the GNAT attribute |
| ``To_Address``. |
| |
| Another issue with address clauses is the interaction with alignment |
| requirements. When an address clause is given for an object, the address |
| value must be consistent with the alignment of the object (which is usually |
| the same as the alignment of the type of the object). If an address clause |
| is given that specifies an inappropriately aligned address value, then the |
| program execution is erroneous. |
| |
| Since this source of erroneous behavior can have unfortunate effects on |
| machines with strict alignment requirements, GNAT |
| checks (at compile time if possible, generating a warning, or at execution |
| time with a run-time check) that the alignment is appropriate. If the |
| run-time check fails, then ``Program_Error`` is raised. This run-time |
| check is suppressed if range checks are suppressed, or if the special GNAT |
| check Alignment_Check is suppressed, or if |
| ``pragma Restrictions (No_Elaboration_Code)`` is in effect. It is also |
| suppressed by default on non-strict alignment machines (such as the x86). |
| |
| In some cases, GNAT does not support an address specification (using either |
| form of aspect specification syntax) for the declaration of an object that has |
| an indefinite nominal subtype. An object declaration has an indefinite |
| nominal subtype if it takes its bounds (for an array type), discriminant |
| values (for a discriminated type whose discriminants lack defaults), or tag |
| (for a class-wide type) from its initial value, as in |
| |
| .. code-block:: ada |
| |
| X : String := Some_Function_Call; |
| -- String has no constraint, so bounds for X come from function call |
| |
| This restriction does not apply if the size of the object's initial value is |
| known at compile time and the type of the object is not class-wide. |
| |
| .. index:: Export |
| |
| An address clause cannot be given for an exported object. More |
| understandably the real restriction is that objects with an address |
| clause cannot be exported. This is because such variables are not |
| defined by the Ada program, so there is no external object to export. |
| |
| .. index:: Import |
| |
| It is permissible to give an address clause and a pragma Import for the |
| same object. In this case, the variable is not really defined by the |
| Ada program, so there is no external symbol to be linked. The link name |
| and the external name are ignored in this case. The reason that we allow this |
| combination is that it provides a useful idiom to avoid unwanted |
| initializations on objects with address clauses. |
| |
| When an address clause is given for an object that has implicit or |
| explicit initialization, then by default initialization takes place. This |
| means that the effect of the object declaration is to overwrite the |
| memory at the specified address. This is almost always not what the |
| programmer wants, so GNAT will output a warning: |
| |
| :: |
| |
| with System; |
| package G is |
| type R is record |
| M : Integer := 0; |
| end record; |
| |
| Ext : R; |
| for Ext'Address use System'To_Address (16#1234_1234#); |
| | |
| >>> warning: implicit initialization of "Ext" may |
| modify overlaid storage |
| >>> warning: use pragma Import for "Ext" to suppress |
| initialization (RM B(24)) |
| |
| end G; |
| |
| As indicated by the warning message, the solution is to use a (dummy) pragma |
| Import to suppress this initialization. The pragma tell the compiler that the |
| object is declared and initialized elsewhere. The following package compiles |
| without warnings (and the initialization is suppressed): |
| |
| .. code-block:: ada |
| |
| with System; |
| package G is |
| type R is record |
| M : Integer := 0; |
| end record; |
| |
| Ext : R; |
| for Ext'Address use System'To_Address (16#1234_1234#); |
| pragma Import (Ada, Ext); |
| end G; |
| |
| |
| A final issue with address clauses involves their use for overlaying |
| variables, as in the following example: |
| |
| .. index:: Overlaying of objects |
| |
| .. code-block:: ada |
| |
| A : Integer; |
| B : Integer; |
| for B'Address use A'Address; |
| |
| |
| or alternatively, using the form recommended by the RM: |
| |
| .. code-block:: ada |
| |
| A : Integer; |
| Addr : constant Address := A'Address; |
| B : Integer; |
| for B'Address use Addr; |
| |
| |
| In both of these cases, ``A`` and ``B`` become aliased to one another |
| via the address clause. This use of address clauses to overlay |
| variables, achieving an effect similar to unchecked conversion |
| was erroneous in Ada 83, but in Ada 95 and Ada 2005 |
| the effect is implementation defined. Furthermore, the |
| Ada RM specifically recommends that in a situation |
| like this, ``B`` should be subject to the following |
| implementation advice (RM 13.3(19)): |
| |
| "19 If the Address of an object is specified, or it is imported |
| or exported, then the implementation should not perform |
| optimizations based on assumptions of no aliases." |
| |
| GNAT follows this recommendation, and goes further by also applying |
| this recommendation to the overlaid variable (``A`` in the above example) |
| in this case. This means that the overlay works "as expected", in that |
| a modification to one of the variables will affect the value of the other. |
| |
| More generally, GNAT interprets this recommendation conservatively for |
| address clauses: in the cases other than overlays, it considers that the |
| object is effectively subject to pragma ``Volatile`` and implements the |
| associated semantics. |
| |
| Note that when address clause overlays are used in this way, there is an |
| issue of unintentional initialization, as shown by this example: |
| |
| :: |
| |
| package Overwrite_Record is |
| type R is record |
| A : Character := 'C'; |
| B : Character := 'A'; |
| end record; |
| X : Short_Integer := 3; |
| Y : R; |
| for Y'Address use X'Address; |
| | |
| >>> warning: default initialization of "Y" may |
| modify "X", use pragma Import for "Y" to |
| suppress initialization (RM B.1(24)) |
| |
| end Overwrite_Record; |
| |
| Here the default initialization of ``Y`` will clobber the value |
| of ``X``, which justifies the warning. The warning notes that |
| this effect can be eliminated by adding a ``pragma Import`` |
| which suppresses the initialization: |
| |
| .. code-block:: ada |
| |
| package Overwrite_Record is |
| type R is record |
| A : Character := 'C'; |
| B : Character := 'A'; |
| end record; |
| X : Short_Integer := 3; |
| Y : R; |
| for Y'Address use X'Address; |
| pragma Import (Ada, Y); |
| end Overwrite_Record; |
| |
| |
| Note that the use of ``pragma Initialize_Scalars`` may cause variables to |
| be initialized when they would not otherwise have been in the absence |
| of the use of this pragma. This may cause an overlay to have this |
| unintended clobbering effect. The compiler avoids this for scalar |
| types, but not for composite objects (where in general the effect |
| of ``Initialize_Scalars`` is part of the initialization routine |
| for the composite object): |
| |
| :: |
| |
| pragma Initialize_Scalars; |
| with Ada.Text_IO; use Ada.Text_IO; |
| procedure Overwrite_Array is |
| type Arr is array (1 .. 5) of Integer; |
| X : Arr := (others => 1); |
| A : Arr; |
| for A'Address use X'Address; |
| | |
| >>> warning: default initialization of "A" may |
| modify "X", use pragma Import for "A" to |
| suppress initialization (RM B.1(24)) |
| |
| begin |
| if X /= Arr'(others => 1) then |
| Put_Line ("X was clobbered"); |
| else |
| Put_Line ("X was not clobbered"); |
| end if; |
| end Overwrite_Array; |
| |
| The above program generates the warning as shown, and at execution |
| time, prints ``X was clobbered``. If the ``pragma Import`` is |
| added as suggested: |
| |
| .. code-block:: ada |
| |
| pragma Initialize_Scalars; |
| with Ada.Text_IO; use Ada.Text_IO; |
| procedure Overwrite_Array is |
| type Arr is array (1 .. 5) of Integer; |
| X : Arr := (others => 1); |
| A : Arr; |
| for A'Address use X'Address; |
| pragma Import (Ada, A); |
| begin |
| if X /= Arr'(others => 1) then |
| Put_Line ("X was clobbered"); |
| else |
| Put_Line ("X was not clobbered"); |
| end if; |
| end Overwrite_Array; |
| |
| then the program compiles without the warning and when run will generate |
| the output ``X was not clobbered``. |
| |
| |
| .. _Use_of_Address_Clauses_for_Memory-Mapped_I/O: |
| |
| Use of Address Clauses for Memory-Mapped I/O |
| ============================================ |
| |
| .. index:: Memory-mapped I/O |
| |
| A common pattern is to use an address clause to map an atomic variable to |
| a location in memory that corresponds to a memory-mapped I/O operation or |
| operations, for example: |
| |
| .. code-block:: ada |
| |
| type Mem_Word is record |
| A,B,C,D : Byte; |
| end record; |
| pragma Atomic (Mem_Word); |
| for Mem_Word_Size use 32; |
| |
| Mem : Mem_Word; |
| for Mem'Address use some-address; |
| ... |
| Temp := Mem; |
| Temp.A := 32; |
| Mem := Temp; |
| |
| For a full access (reference or modification) of the variable (Mem) in this |
| case, as in the above examples, GNAT guarantees that the entire atomic word |
| will be accessed, in accordance with the RM C.6(15) clause. |
| |
| A problem arises with a component access such as: |
| |
| .. code-block:: ada |
| |
| Mem.A := 32; |
| |
| Note that the component A is not declared as atomic. This means that it is |
| not clear what this assignment means. It could correspond to full word read |
| and write as given in the first example, or on architectures that supported |
| such an operation it might be a single byte store instruction. The RM does |
| not have anything to say in this situation, and GNAT does not make any |
| guarantee. The code generated may vary from target to target. GNAT will issue |
| a warning in such a case: |
| |
| :: |
| |
| Mem.A := 32; |
| | |
| >>> warning: access to non-atomic component of atomic array, |
| may cause unexpected accesses to atomic object |
| |
| It is best to be explicit in this situation, by either declaring the |
| components to be atomic if you want the byte store, or explicitly writing |
| the full word access sequence if that is what the hardware requires. |
| Alternatively, if the full word access sequence is required, GNAT also |
| provides the pragma ``Volatile_Full_Access`` which can be used in lieu of |
| pragma ``Atomic`` and will give the additional guarantee. |
| |
| |
| .. _Effect_of_Convention_on_Representation: |
| |
| Effect of Convention on Representation |
| ====================================== |
| |
| .. index:: Convention, effect on representation |
| |
| Normally the specification of a foreign language convention for a type or |
| an object has no effect on the chosen representation. In particular, the |
| representation chosen for data in GNAT generally meets the standard system |
| conventions, and for example records are laid out in a manner that is |
| consistent with C. This means that specifying convention C (for example) |
| has no effect. |
| |
| There are four exceptions to this general rule: |
| |
| * *Convention Fortran and array subtypes*. |
| |
| If pragma Convention Fortran is specified for an array subtype, then in |
| accordance with the implementation advice in section 3.6.2(11) of the |
| Ada Reference Manual, the array will be stored in a Fortran-compatible |
| column-major manner, instead of the normal default row-major order. |
| |
| * *Convention C and enumeration types* |
| |
| GNAT normally stores enumeration types in 8, 16, or 32 bits as required |
| to accommodate all values of the type. For example, for the enumeration |
| type declared by: |
| |
| :: |
| |
| type Color is (Red, Green, Blue); |
| |
| 8 bits is sufficient to store all values of the type, so by default, objects |
| of type ``Color`` will be represented using 8 bits. However, normal C |
| convention is to use 32 bits for all enum values in C, since enum values |
| are essentially of type int. If pragma ``Convention C`` is specified for an |
| Ada enumeration type, then the size is modified as necessary (usually to |
| 32 bits) to be consistent with the C convention for enum values. |
| |
| Note that this treatment applies only to types. If Convention C is given for |
| an enumeration object, where the enumeration type is not Convention C, then |
| Object_Size bits are allocated. For example, for a normal enumeration type, |
| with less than 256 elements, only 8 bits will be allocated for the object. |
| Since this may be a surprise in terms of what C expects, GNAT will issue a |
| warning in this situation. The warning can be suppressed by giving an explicit |
| size clause specifying the desired size. |
| |
| * *Convention C/Fortran and Boolean types* |
| |
| In C, the usual convention for boolean values, that is values used for |
| conditions, is that zero represents false, and nonzero values represent |
| true. In Ada, the normal convention is that two specific values, typically |
| 0/1, are used to represent false/true respectively. |
| |
| Fortran has a similar convention for ``LOGICAL`` values (any nonzero |
| value represents true). |
| |
| To accommodate the Fortran and C conventions, if a pragma Convention specifies |
| C or Fortran convention for a derived Boolean, as in the following example: |
| |
| :: |
| |
| type C_Switch is new Boolean; |
| pragma Convention (C, C_Switch); |
| |
| |
| then the GNAT generated code will treat any nonzero value as true. For truth |
| values generated by GNAT, the conventional value 1 will be used for True, but |
| when one of these values is read, any nonzero value is treated as True. |
| |
| |
| .. _Conventions_and_Anonymous_Access_Types: |
| |
| Conventions and Anonymous Access Types |
| ====================================== |
| |
| .. index:: Anonymous access types |
| |
| .. index:: Convention for anonymous access types |
| |
| The RM is not entirely clear on convention handling in a number of cases, |
| and in particular, it is not clear on the convention to be given to |
| anonymous access types in general, and in particular what is to be |
| done for the case of anonymous access-to-subprogram. |
| |
| In GNAT, we decide that if an explicit Convention is applied |
| to an object or component, and its type is such an anonymous type, |
| then the convention will apply to this anonymous type as well. This |
| seems to make sense since it is anomolous in any case to have a |
| different convention for an object and its type, and there is clearly |
| no way to explicitly specify a convention for an anonymous type, since |
| it doesn't have a name to specify! |
| |
| Furthermore, we decide that if a convention is applied to a record type, |
| then this convention is inherited by any of its components that are of an |
| anonymous access type which do not have an explicitly specified convention. |
| |
| The following program shows these conventions in action: |
| |
| :: |
| |
| package ConvComp is |
| type Foo is range 1 .. 10; |
| type T1 is record |
| A : access function (X : Foo) return Integer; |
| B : Integer; |
| end record; |
| pragma Convention (C, T1); |
| |
| type T2 is record |
| A : access function (X : Foo) return Integer; |
| pragma Convention (C, A); |
| B : Integer; |
| end record; |
| pragma Convention (COBOL, T2); |
| |
| type T3 is record |
| A : access function (X : Foo) return Integer; |
| pragma Convention (COBOL, A); |
| B : Integer; |
| end record; |
| pragma Convention (C, T3); |
| |
| type T4 is record |
| A : access function (X : Foo) return Integer; |
| B : Integer; |
| end record; |
| pragma Convention (COBOL, T4); |
| |
| function F (X : Foo) return Integer; |
| pragma Convention (C, F); |
| |
| function F (X : Foo) return Integer is (13); |
| |
| TV1 : T1 := (F'Access, 12); -- OK |
| TV2 : T2 := (F'Access, 13); -- OK |
| |
| TV3 : T3 := (F'Access, 13); -- ERROR |
| | |
| >>> subprogram "F" has wrong convention |
| >>> does not match access to subprogram declared at line 17 |
| 38. TV4 : T4 := (F'Access, 13); -- ERROR |
| | |
| >>> subprogram "F" has wrong convention |
| >>> does not match access to subprogram declared at line 24 |
| 39. end ConvComp; |
| |
| |
| .. _Determining_the_Representations_chosen_by_GNAT: |
| |
| Determining the Representations chosen by GNAT |
| ============================================== |
| |
| .. index:: Representation, determination of |
| |
| .. index:: -gnatR (gcc) |
| |
| Although the descriptions in this section are intended to be complete, it is |
| often easier to simply experiment to see what GNAT accepts and what the |
| effect is on the layout of types and objects. |
| |
| As required by the Ada RM, if a representation clause is not accepted, then |
| it must be rejected as illegal by the compiler. However, when a |
| representation clause or pragma is accepted, there can still be questions |
| of what the compiler actually does. For example, if a partial record |
| representation clause specifies the location of some components and not |
| others, then where are the non-specified components placed? Or if pragma |
| ``Pack`` is used on a record, then exactly where are the resulting |
| fields placed? The section on pragma ``Pack`` in this chapter can be |
| used to answer the second question, but it is often easier to just see |
| what the compiler does. |
| |
| For this purpose, GNAT provides the option *-gnatR*. If you compile |
| with this option, then the compiler will output information on the actual |
| representations chosen, in a format similar to source representation |
| clauses. For example, if we compile the package: |
| |
| .. code-block:: ada |
| |
| package q is |
| type r (x : boolean) is tagged record |
| case x is |
| when True => S : String (1 .. 100); |
| when False => null; |
| end case; |
| end record; |
| |
| type r2 is new r (false) with record |
| y2 : integer; |
| end record; |
| |
| for r2 use record |
| y2 at 16 range 0 .. 31; |
| end record; |
| |
| type x is record |
| y : character; |
| end record; |
| |
| type x1 is array (1 .. 10) of x; |
| for x1'component_size use 11; |
| |
| type ia is access integer; |
| |
| type Rb1 is array (1 .. 13) of Boolean; |
| pragma Pack (rb1); |
| |
| type Rb2 is array (1 .. 65) of Boolean; |
| pragma Pack (rb2); |
| |
| type x2 is record |
| l1 : Boolean; |
| l2 : Duration; |
| l3 : Float; |
| l4 : Boolean; |
| l5 : Rb1; |
| l6 : Rb2; |
| end record; |
| pragma Pack (x2); |
| end q; |
| |
| using the switch *-gnatR* we obtain the following output: |
| |
| .. code-block:: ada |
| |
| Representation information for unit q |
| ------------------------------------- |
| |
| for r'Size use ??; |
| for r'Alignment use 4; |
| for r use record |
| x at 4 range 0 .. 7; |
| _tag at 0 range 0 .. 31; |
| s at 5 range 0 .. 799; |
| end record; |
| |
| for r2'Size use 160; |
| for r2'Alignment use 4; |
| for r2 use record |
| x at 4 range 0 .. 7; |
| _tag at 0 range 0 .. 31; |
| _parent at 0 range 0 .. 63; |
| y2 at 16 range 0 .. 31; |
| end record; |
| |
| for x'Size use 8; |
| for x'Alignment use 1; |
| for x use record |
| y at 0 range 0 .. 7; |
| end record; |
| |
| for x1'Size use 112; |
| for x1'Alignment use 1; |
| for x1'Component_Size use 11; |
| |
| for rb1'Size use 13; |
| for rb1'Alignment use 2; |
| for rb1'Component_Size use 1; |
| |
| for rb2'Size use 72; |
| for rb2'Alignment use 1; |
| for rb2'Component_Size use 1; |
| |
| for x2'Size use 224; |
| for x2'Alignment use 4; |
| for x2 use record |
| l1 at 0 range 0 .. 0; |
| l2 at 0 range 1 .. 64; |
| l3 at 12 range 0 .. 31; |
| l4 at 16 range 0 .. 0; |
| l5 at 16 range 1 .. 13; |
| l6 at 18 range 0 .. 71; |
| end record; |
| |
| The Size values are actually the Object_Size, i.e., the default size that |
| will be allocated for objects of the type. |
| The ``??`` size for type r indicates that we have a variant record, and the |
| actual size of objects will depend on the discriminant value. |
| |
| The Alignment values show the actual alignment chosen by the compiler |
| for each record or array type. |
| |
| The record representation clause for type r shows where all fields |
| are placed, including the compiler generated tag field (whose location |
| cannot be controlled by the programmer). |
| |
| The record representation clause for the type extension r2 shows all the |
| fields present, including the parent field, which is a copy of the fields |
| of the parent type of r2, i.e., r1. |
| |
| The component size and size clauses for types rb1 and rb2 show |
| the exact effect of pragma ``Pack`` on these arrays, and the record |
| representation clause for type x2 shows how pragma `Pack` affects |
| this record type. |
| |
| In some cases, it may be useful to cut and paste the representation clauses |
| generated by the compiler into the original source to fix and guarantee |
| the actual representation to be used. |