Mercurial > hg > CbC > CbC_gcc
diff gcc/ada/doc/gnat_rm/implementation_of_specific_ada_features.rst @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | |
children | 1830386684a0 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gcc/ada/doc/gnat_rm/implementation_of_specific_ada_features.rst Fri Oct 27 22:46:09 2017 +0900 @@ -0,0 +1,703 @@ +.. _Implementation_of_Specific_Ada_Features: + +*************************************** +Implementation of Specific Ada Features +*************************************** + +This chapter describes the GNAT implementation of several Ada language +facilities. + +.. _Machine_Code_Insertions: + +Machine Code Insertions +======================= + +.. index:: Machine Code insertions + +Package ``Machine_Code`` provides machine code support as described +in the Ada Reference Manual in two separate forms: + +* + Machine code statements, consisting of qualified expressions that + fit the requirements of RM section 13.8. +* + An intrinsic callable procedure, providing an alternative mechanism of + including machine instructions in a subprogram. + +The two features are similar, and both are closely related to the mechanism +provided by the asm instruction in the GNU C compiler. Full understanding +and use of the facilities in this package requires understanding the asm +instruction, see the section on Extended Asm in +:title:`Using_the_GNU_Compiler_Collection_(GCC)`. + +Calls to the function ``Asm`` and the procedure ``Asm`` have identical +semantic restrictions and effects as described below. Both are provided so +that the procedure call can be used as a statement, and the function call +can be used to form a code_statement. + +Consider this C ``asm`` instruction: + +:: + + asm ("fsinx %1 %0" : "=f" (result) : "f" (angle)); + + +The equivalent can be written for GNAT as: + +.. code-block:: ada + + Asm ("fsinx %1 %0", + My_Float'Asm_Output ("=f", result), + My_Float'Asm_Input ("f", angle)); + + +The first argument to ``Asm`` is the assembler template, and is +identical to what is used in GNU C. This string must be a static +expression. The second argument is the output operand list. It is +either a single ``Asm_Output`` attribute reference, or a list of such +references enclosed in parentheses (technically an array aggregate of +such references). + +The ``Asm_Output`` attribute denotes a function that takes two +parameters. The first is a string, the second is the name of a variable +of the type designated by the attribute prefix. The first (string) +argument is required to be a static expression and designates the +constraint (see the section on Constraints in +:title:`Using_the_GNU_Compiler_Collection_(GCC)`) +for the parameter; e.g., what kind of register is required. The second +argument is the variable to be written or updated with the +result. The possible values for constraint are the same as those used in +the RTL, and are dependent on the configuration file used to build the +GCC back end. If there are no output operands, then this argument may +either be omitted, or explicitly given as ``No_Output_Operands``. +No support is provided for GNU C's symbolic names for output parameters. + +The second argument of ``my_float'Asm_Output`` functions as +though it were an ``out`` parameter, which is a little curious, but +all names have the form of expressions, so there is no syntactic +irregularity, even though normally functions would not be permitted +``out`` parameters. The third argument is the list of input +operands. It is either a single ``Asm_Input`` attribute reference, or +a list of such references enclosed in parentheses (technically an array +aggregate of such references). + +The ``Asm_Input`` attribute denotes a function that takes two +parameters. The first is a string, the second is an expression of the +type designated by the prefix. The first (string) argument is required +to be a static expression, and is the constraint for the parameter, +(e.g., what kind of register is required). The second argument is the +value to be used as the input argument. The possible values for the +constraint are the same as those used in the RTL, and are dependent on +the configuration file used to built the GCC back end. +No support is provided for GNU C's symbolic names for input parameters. + +If there are no input operands, this argument may either be omitted, or +explicitly given as ``No_Input_Operands``. The fourth argument, not +present in the above example, is a list of register names, called the +*clobber* argument. This argument, if given, must be a static string +expression, and is a space or comma separated list of names of registers +that must be considered destroyed as a result of the ``Asm`` call. If +this argument is the null string (the default value), then the code +generator assumes that no additional registers are destroyed. +In addition to registers, the special clobbers ``memory`` and +``cc`` as described in the GNU C docs are both supported. + +The fifth argument, not present in the above example, called the +*volatile* argument, is by default ``False``. It can be set to +the literal value ``True`` to indicate to the code generator that all +optimizations with respect to the instruction specified should be +suppressed, and in particular an instruction that has outputs +will still be generated, even if none of the outputs are +used. See :title:`Using_the_GNU_Compiler_Collection_(GCC)` +for the full description. +Generally it is strongly advisable to use Volatile for any ASM statement +that is missing either input or output operands or to avoid unwanted +optimizations. A warning is generated if this advice is not followed. + +No support is provided for GNU C's ``asm goto`` feature. + +The ``Asm`` subprograms may be used in two ways. First the procedure +forms can be used anywhere a procedure call would be valid, and +correspond to what the RM calls 'intrinsic' routines. Such calls can +be used to intersperse machine instructions with other Ada statements. +Second, the function forms, which return a dummy value of the limited +private type ``Asm_Insn``, can be used in code statements, and indeed +this is the only context where such calls are allowed. Code statements +appear as aggregates of the form: + +.. code-block:: ada + + Asm_Insn'(Asm (...)); + Asm_Insn'(Asm_Volatile (...)); + +In accordance with RM rules, such code statements are allowed only +within subprograms whose entire body consists of such statements. It is +not permissible to intermix such statements with other Ada statements. + +Typically the form using intrinsic procedure calls is more convenient +and more flexible. The code statement form is provided to meet the RM +suggestion that such a facility should be made available. The following +is the exact syntax of the call to ``Asm``. As usual, if named notation +is used, the arguments may be given in arbitrary order, following the +normal rules for use of positional and named arguments: + +:: + + ASM_CALL ::= Asm ( + [Template =>] static_string_EXPRESSION + [,[Outputs =>] OUTPUT_OPERAND_LIST ] + [,[Inputs =>] INPUT_OPERAND_LIST ] + [,[Clobber =>] static_string_EXPRESSION ] + [,[Volatile =>] static_boolean_EXPRESSION] ) + + OUTPUT_OPERAND_LIST ::= + [PREFIX.]No_Output_Operands + | OUTPUT_OPERAND_ATTRIBUTE + | (OUTPUT_OPERAND_ATTRIBUTE {,OUTPUT_OPERAND_ATTRIBUTE}) + + OUTPUT_OPERAND_ATTRIBUTE ::= + SUBTYPE_MARK'Asm_Output (static_string_EXPRESSION, NAME) + + INPUT_OPERAND_LIST ::= + [PREFIX.]No_Input_Operands + | INPUT_OPERAND_ATTRIBUTE + | (INPUT_OPERAND_ATTRIBUTE {,INPUT_OPERAND_ATTRIBUTE}) + + INPUT_OPERAND_ATTRIBUTE ::= + SUBTYPE_MARK'Asm_Input (static_string_EXPRESSION, EXPRESSION) + +The identifiers ``No_Input_Operands`` and ``No_Output_Operands`` +are declared in the package ``Machine_Code`` and must be referenced +according to normal visibility rules. In particular if there is no +``use`` clause for this package, then appropriate package name +qualification is required. + +.. _GNAT_Implementation_of_Tasking: + +GNAT Implementation of Tasking +============================== + +This chapter outlines the basic GNAT approach to tasking (in particular, +a multi-layered library for portability) and discusses issues related +to compliance with the Real-Time Systems Annex. + +.. _Mapping_Ada_Tasks_onto_the_Underlying_Kernel_Threads: + +Mapping Ada Tasks onto the Underlying Kernel Threads +---------------------------------------------------- + +GNAT's run-time support comprises two layers: + +* GNARL (GNAT Run-time Layer) +* GNULL (GNAT Low-level Library) + +In GNAT, Ada's tasking services rely on a platform and OS independent +layer known as GNARL. This code is responsible for implementing the +correct semantics of Ada's task creation, rendezvous, protected +operations etc. + +GNARL decomposes Ada's tasking semantics into simpler lower level +operations such as create a thread, set the priority of a thread, +yield, create a lock, lock/unlock, etc. The spec for these low-level +operations constitutes GNULLI, the GNULL Interface. This interface is +directly inspired from the POSIX real-time API. + +If the underlying executive or OS implements the POSIX standard +faithfully, the GNULL Interface maps as is to the services offered by +the underlying kernel. Otherwise, some target dependent glue code maps +the services offered by the underlying kernel to the semantics expected +by GNARL. + +Whatever the underlying OS (VxWorks, UNIX, Windows, etc.) the +key point is that each Ada task is mapped on a thread in the underlying +kernel. For example, in the case of VxWorks, one Ada task = one VxWorks task. + +In addition Ada task priorities map onto the underlying thread priorities. +Mapping Ada tasks onto the underlying kernel threads has several advantages: + +* + The underlying scheduler is used to schedule the Ada tasks. This + makes Ada tasks as efficient as kernel threads from a scheduling + standpoint. + +* + Interaction with code written in C containing threads is eased + since at the lowest level Ada tasks and C threads map onto the same + underlying kernel concept. + +* + When an Ada task is blocked during I/O the remaining Ada tasks are + able to proceed. + +* + On multiprocessor systems Ada tasks can execute in parallel. + +Some threads libraries offer a mechanism to fork a new process, with the +child process duplicating the threads from the parent. +GNAT does not +support this functionality when the parent contains more than one task. + +.. index:: Forking a new process + +.. _Ensuring_Compliance_with_the_Real-Time_Annex: + +Ensuring Compliance with the Real-Time Annex +-------------------------------------------- + +.. index:: Real-Time Systems Annex compliance + +Although mapping Ada tasks onto +the underlying threads has significant advantages, it does create some +complications when it comes to respecting the scheduling semantics +specified in the real-time annex (Annex D). + +For instance the Annex D requirement for the ``FIFO_Within_Priorities`` +scheduling policy states: + + *When the active priority of a ready task that is not running + changes, or the setting of its base priority takes effect, the + task is removed from the ready queue for its old active priority + and is added at the tail of the ready queue for its new active + priority, except in the case where the active priority is lowered + due to the loss of inherited priority, in which case the task is + added at the head of the ready queue for its new active priority.* + +While most kernels do put tasks at the end of the priority queue when +a task changes its priority, (which respects the main +FIFO_Within_Priorities requirement), almost none keep a thread at the +beginning of its priority queue when its priority drops from the loss +of inherited priority. + +As a result most vendors have provided incomplete Annex D implementations. + +The GNAT run-time, has a nice cooperative solution to this problem +which ensures that accurate FIFO_Within_Priorities semantics are +respected. + +The principle is as follows. When an Ada task T is about to start +running, it checks whether some other Ada task R with the same +priority as T has been suspended due to the loss of priority +inheritance. If this is the case, T yields and is placed at the end of +its priority queue. When R arrives at the front of the queue it +executes. + +Note that this simple scheme preserves the relative order of the tasks +that were ready to execute in the priority queue where R has been +placed at the end. + +.. Support_for_Locking_Policies + +Support for Locking Policies +---------------------------- + +This section specifies which policies specified by pragma Locking_Policy +are supported on which platforms. + +GNAT supports the standard ``Ceiling_Locking`` policy, and the +implementation defined ``Inheritance_Locking`` and +``Concurrent_Readers_Locking`` policies. + +``Ceiling_Locking`` is supported on all platforms if the operating system +supports it. In particular, ``Ceiling_Locking`` is not supported on +VxWorks. +``Inheritance_Locking`` is supported on +Linux, +Darwin (Mac OS X), +LynxOS 178, +and VxWorks. +``Concurrent_Readers_Locking`` is supported on Linux. + +Notes about ``Ceiling_Locking`` on Linux: +If the process is running as 'root', ceiling locking is used. +If the capabilities facility is installed +("sudo apt-get --assume-yes install libcap-dev" on Ubuntu, +for example), +and the program is linked against that library +("-largs -lcap"), +and the executable file has the cap_sys_nice capability +("sudo /sbin/setcap cap_sys_nice=ep executable_file_name"), +then ceiling locking is used. +Otherwise, the ``Ceiling_Locking`` policy is ignored. + +.. _GNAT_Implementation_of_Shared_Passive_Packages: + +GNAT Implementation of Shared Passive Packages +============================================== + +.. index:: Shared passive packages + +GNAT fully implements the :index:`pragma <pragma Shared_Passive>` +``Shared_Passive`` for +the purpose of designating shared passive packages. +This allows the use of passive partitions in the +context described in the Ada Reference Manual; i.e., for communication +between separate partitions of a distributed application using the +features in Annex E. + +.. index:: Annex E + +.. index:: Distribution Systems Annex + +However, the implementation approach used by GNAT provides for more +extensive usage as follows: + +*Communication between separate programs* + This allows separate programs to access the data in passive + partitions, using protected objects for synchronization where + needed. The only requirement is that the two programs have a + common shared file system. It is even possible for programs + running on different machines with different architectures + (e.g., different endianness) to communicate via the data in + a passive partition. + +*Persistence between program runs* + The data in a passive package can persist from one run of a + program to another, so that a later program sees the final + values stored by a previous run of the same program. + +The implementation approach used is to store the data in files. A +separate stream file is created for each object in the package, and +an access to an object causes the corresponding file to be read or +written. + +.. index:: SHARED_MEMORY_DIRECTORY environment variable + +The environment variable ``SHARED_MEMORY_DIRECTORY`` should be +set to the directory to be used for these files. +The files in this directory +have names that correspond to their fully qualified names. For +example, if we have the package + +.. code-block:: ada + + package X is + pragma Shared_Passive (X); + Y : Integer; + Z : Float; + end X; + +and the environment variable is set to ``/stemp/``, then the files created +will have the names: + +:: + + /stemp/x.y + /stemp/x.z + + +These files are created when a value is initially written to the object, and +the files are retained until manually deleted. This provides the persistence +semantics. If no file exists, it means that no partition has assigned a value +to the variable; in this case the initial value declared in the package +will be used. This model ensures that there are no issues in synchronizing +the elaboration process, since elaboration of passive packages elaborates the +initial values, but does not create the files. + +The files are written using normal ``Stream_IO`` access. +If you want to be able +to communicate between programs or partitions running on different +architectures, then you should use the XDR versions of the stream attribute +routines, since these are architecture independent. + +If active synchronization is required for access to the variables in the +shared passive package, then as described in the Ada Reference Manual, the +package may contain protected objects used for this purpose. In this case +a lock file (whose name is :file:`___lock` (three underscores) +is created in the shared memory directory. + +.. index:: ___lock file (for shared passive packages) + +This is used to provide the required locking +semantics for proper protected object synchronization. + +GNAT supports shared passive packages on all platforms +except for OpenVMS. + +.. _Code_Generation_for_Array_Aggregates: + +Code Generation for Array Aggregates +==================================== + +Aggregates have a rich syntax and allow the user to specify the values of +complex data structures by means of a single construct. As a result, the +code generated for aggregates can be quite complex and involve loops, case +statements and multiple assignments. In the simplest cases, however, the +compiler will recognize aggregates whose components and constraints are +fully static, and in those cases the compiler will generate little or no +executable code. The following is an outline of the code that GNAT generates +for various aggregate constructs. For further details, you will find it +useful to examine the output produced by the -gnatG flag to see the expanded +source that is input to the code generator. You may also want to examine +the assembly code generated at various levels of optimization. + +The code generated for aggregates depends on the context, the component values, +and the type. In the context of an object declaration the code generated is +generally simpler than in the case of an assignment. As a general rule, static +component values and static subtypes also lead to simpler code. + +.. _Static_constant_aggregates_with_static_bounds: + +Static constant aggregates with static bounds +--------------------------------------------- + +For the declarations: + +.. code-block:: ada + + type One_Dim is array (1..10) of integer; + ar0 : constant One_Dim := (1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + + +GNAT generates no executable code: the constant ar0 is placed in static memory. +The same is true for constant aggregates with named associations: + + +.. code-block:: ada + + Cr1 : constant One_Dim := (4 => 16, 2 => 4, 3 => 9, 1 => 1, 5 .. 10 => 0); + Cr3 : constant One_Dim := (others => 7777); + + +The same is true for multidimensional constant arrays such as: + +.. code-block:: ada + + type two_dim is array (1..3, 1..3) of integer; + Unit : constant two_dim := ( (1,0,0), (0,1,0), (0,0,1)); + + +The same is true for arrays of one-dimensional arrays: the following are +static: + + +.. code-block:: ada + + type ar1b is array (1..3) of boolean; + type ar_ar is array (1..3) of ar1b; + None : constant ar1b := (others => false); -- fully static + None2 : constant ar_ar := (1..3 => None); -- fully static + + +However, for multidimensional aggregates with named associations, GNAT will +generate assignments and loops, even if all associations are static. The +following two declarations generate a loop for the first dimension, and +individual component assignments for the second dimension: + + +.. code-block:: ada + + Zero1: constant two_dim := (1..3 => (1..3 => 0)); + Zero2: constant two_dim := (others => (others => 0)); + + +.. _Constant_aggregates_with_unconstrained_nominal_types: + +Constant aggregates with unconstrained nominal types +---------------------------------------------------- + +In such cases the aggregate itself establishes the subtype, so that +associations with ``others`` cannot be used. GNAT determines the +bounds for the actual subtype of the aggregate, and allocates the +aggregate statically as well. No code is generated for the following: + + +.. code-block:: ada + + type One_Unc is array (natural range <>) of integer; + Cr_Unc : constant One_Unc := (12,24,36); + + +.. _Aggregates_with_static_bounds: + +Aggregates with static bounds +----------------------------- + +In all previous examples the aggregate was the initial (and immutable) value +of a constant. If the aggregate initializes a variable, then code is generated +for it as a combination of individual assignments and loops over the target +object. The declarations + + +.. code-block:: ada + + Cr_Var1 : One_Dim := (2, 5, 7, 11, 0, 0, 0, 0, 0, 0); + Cr_Var2 : One_Dim := (others > -1); + + +generate the equivalent of + + +.. code-block:: ada + + Cr_Var1 (1) := 2; + Cr_Var1 (2) := 3; + Cr_Var1 (3) := 5; + Cr_Var1 (4) := 11; + + for I in Cr_Var2'range loop + Cr_Var2 (I) := -1; + end loop; + + +.. _Aggregates_with_nonstatic_bounds: + +Aggregates with nonstatic bounds +--------------------------------- + +If the bounds of the aggregate are not statically compatible with the bounds +of the nominal subtype of the target, then constraint checks have to be +generated on the bounds. For a multidimensional array, constraint checks may +have to be applied to sub-arrays individually, if they do not have statically +compatible subtypes. + +.. _Aggregates_in_assignment_statements: + +Aggregates in assignment statements +----------------------------------- + +In general, aggregate assignment requires the construction of a temporary, +and a copy from the temporary to the target of the assignment. This is because +it is not always possible to convert the assignment into a series of individual +component assignments. For example, consider the simple case: + + +.. code-block:: ada + + A := (A(2), A(1)); + + +This cannot be converted into: + + +.. code-block:: ada + + A(1) := A(2); + A(2) := A(1); + + +So the aggregate has to be built first in a separate location, and then +copied into the target. GNAT recognizes simple cases where this intermediate +step is not required, and the assignments can be performed in place, directly +into the target. The following sufficient criteria are applied: + +* + The bounds of the aggregate are static, and the associations are static. +* + The components of the aggregate are static constants, names of + simple variables that are not renamings, or expressions not involving + indexed components whose operands obey these rules. + +If any of these conditions are violated, the aggregate will be built in +a temporary (created either by the front-end or the code generator) and then +that temporary will be copied onto the target. + +.. _The_Size_of_Discriminated_Records_with_Default_Discriminants: + +The Size of Discriminated Records with Default Discriminants +============================================================ + +If a discriminated type ``T`` has discriminants with default values, it is +possible to declare an object of this type without providing an explicit +constraint: + + +.. code-block:: ada + + type Size is range 1..100; + + type Rec (D : Size := 15) is record + Name : String (1..D); + end T; + + Word : Rec; + + +Such an object is said to be *unconstrained*. +The discriminant of the object +can be modified by a full assignment to the object, as long as it preserves the +relation between the value of the discriminant, and the value of the components +that depend on it: + + +.. code-block:: ada + + Word := (3, "yes"); + + Word := (5, "maybe"); + + Word := (5, "no"); -- raises Constraint_Error + +In order to support this behavior efficiently, an unconstrained object is +given the maximum size that any value of the type requires. In the case +above, ``Word`` has storage for the discriminant and for +a ``String`` of length 100. +It is important to note that unconstrained objects do not require dynamic +allocation. It would be an improper implementation to place on the heap those +components whose size depends on discriminants. (This improper implementation +was used by some Ada83 compilers, where the ``Name`` component above +would have +been stored as a pointer to a dynamic string). Following the principle that +dynamic storage management should never be introduced implicitly, +an Ada compiler should reserve the full size for an unconstrained declared +object, and place it on the stack. + +This maximum size approach +has been a source of surprise to some users, who expect the default +values of the discriminants to determine the size reserved for an +unconstrained object: "If the default is 15, why should the object occupy +a larger size?" +The answer, of course, is that the discriminant may be later modified, +and its full range of values must be taken into account. This is why the +declaration: + + +.. code-block:: ada + + type Rec (D : Positive := 15) is record + Name : String (1..D); + end record; + + Too_Large : Rec; + +is flagged by the compiler with a warning: +an attempt to create ``Too_Large`` will raise ``Storage_Error``, +because the required size includes ``Positive'Last`` +bytes. As the first example indicates, the proper approach is to declare an +index type of 'reasonable' range so that unconstrained objects are not too +large. + +One final wrinkle: if the object is declared to be ``aliased``, or if it is +created in the heap by means of an allocator, then it is *not* +unconstrained: +it is constrained by the default values of the discriminants, and those values +cannot be modified by full assignment. This is because in the presence of +aliasing all views of the object (which may be manipulated by different tasks, +say) must be consistent, so it is imperative that the object, once created, +remain invariant. + +.. _Strict_Conformance_to_the_Ada_Reference_Manual: + +Strict Conformance to the Ada Reference Manual +============================================== + +The dynamic semantics defined by the Ada Reference Manual impose a set of +run-time checks to be generated. By default, the GNAT compiler will insert many +run-time checks into the compiled code, including most of those required by the +Ada Reference Manual. However, there are two checks that are not enabled in +the default mode for efficiency reasons: checks for access before elaboration +on subprogram calls, and stack overflow checking (most operating systems do not +perform this check by default). + +Strict conformance to the Ada Reference Manual can be achieved by adding two +compiler options for dynamic checks for access-before-elaboration on subprogram +calls and generic instantiations (*-gnatE*), and stack overflow checking +(*-fstack-check*). + +Note that the result of a floating point arithmetic operation in overflow and +invalid situations, when the ``Machine_Overflows`` attribute of the result +type is ``False``, is to generate IEEE NaN and infinite values. This is the +case for machines compliant with the IEEE floating-point standard, but on +machines that are not fully compliant with this standard, such as Alpha, the +*-mieee* compiler flag must be used for achieving IEEE confirming +behavior (although at the cost of a significant performance penalty), so +infinite and NaN values are properly generated.