[flang] Extract Calls.md into its own branch.

Original-commit: flang-compiler/f18@8a5a2c99dc
Reviewed-on: https://github.com/flang-compiler/f18/pull/683
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2019-08-23 13:42:13 -07:00
parent d7443b5ad2
commit 9dcbed4735
1 changed files with 299 additions and 0 deletions

View File

@ -0,0 +1,299 @@
<!--
Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
-->
## Procedure reference implementation protocol
Fortran function and subroutine references are complicated.
This document attempts to collect the requirements imposed by the 2018
standard (and legacy extensions) on programs and implementations, work
through the implications of the various features, and propose both a
runtime model and compiler design.
All section, requirement, and constraint numbers herein pertain to
the Fortran 2018 standard.
This note does not consider calls to intrinsic procedures, statement
functions, or calls to internal runtime support library routines.
## Interfaces
Referenced procedures may or may not have declared interfaces
available to their call sites.
Calls to procedures with some post-'77 features require an explicit interface
(15.4.2.2):
* keyword arguments
* procedures that are `ELEMENTAL` or `BIND(C)`
* procedures that are required to be `PURE` due to the context of the call
(specification expression, `DO CONCURRENT`, `FORALL`)
* dummy arguments with these attributes: `ALLOCATABLE`, `POINTER`,
`VALUE`, `TARGET`, `OPTIONAL`, `ASYNCHRONOUS`, `VOLATILE`
* dummy arguments that are coarrays, have assumed-shape/-rank,
have parameterized derived types, &/or are polymorphic
* function results that are arrays, `ALLOCATABLE`, `POINTER`,
or have derived types with any length parameters that are
neither constant nor assumed
Module procedures, internal procedures, procedure pointers,
type-bound procedures, and recursive references by a procedure to itself
always have explicit interfaces.
Other uses of procedures besides calls may also require explicit interfaces,
such as procedure pointer assignment, type-bound procedure bindings, &c.
### Implicit interfaces
In the absence of any characteristic or context that requires an
explicit interface (see above), a top-level function or subroutine
can be called via its implicit interface.
Each of the arguments can be passed as a simple address, including
dummy procedures.
Procedures that *can* be called via an implicit interface can
enjoy more thorough checking
by semantics when they do have a visible external interface, but must be
compiled as if all calls to them were through the implicit interface.
Procedures that can be called via an implicit interface should respect
the naming conventions and ABI, if any, used for Fortran '77 programs
on the target architecture, so that portable libraries can be compiled
and used by distinct implementations (and versions of implementations)
of Fortran.
Note that functions with implicit interfaces still have known result
types, possibly by means of implicit typing of their names.
They can also be `CHARACTER(*)` assumed-length character functions.
In other words: procedures that can be referenced with implicit interfaces
have argument lists that comprise only addresses of actual arguments,
the length of an assumed-length `CHARACTER(*)` result, and links to
of host variable blocks for dummy procedures (see below), and they can
return only scalar values of intrinsic types.
None of their arguments or results are implemented with descriptors.
## Protocol overview
Here is a summary script of all of the actions that may need to be taken
by the calling procedure and its referenced procedure to effect
the call, entry, exit, and return steps of the procedure reference
protocol.
The order of these steps is not particularly strict, and we have
some design alternatives that are explored further below.
### Before the call:
1. Compute &/or copy into temporary storage the values of
some actual argument expressions and designators (see below).
1. Finalize &/or re-initialize `INTENT(OUT)` non-pointer non-allocatable
actual arguments (see below).
1. Create and populate descriptors for assumed-shape/-rank arrays,
parameterized derived types with length, polymorphic types,
& coarrays.
1. Possibly allocating function result storage,
if its size can be known by the caller; function results that are
neither `POINTER` nor `ALLOCATABLE` must have explicit shapes (C816).
1. Create and populate a descriptor for the function result, if it
needs one (deferred-shape/-length `POINTER`, any `ALLOCATABLE`,
parameterized derived type with non-constant length parameters, &c.).
1. Capture the values of host-escaping local objects in memory;
package them into single address (for calls to internal procedures &
for calls that pass internal procedures as arguments).
1. Resolve the target procedure's polymorphic binding, if any.
1. Marshal actual argument addresses/values into registers.
1. Marshal extra arguments for assumed-length `CHARACTER` result length,
function result descriptor, target host variable link, &/or dummy
procedure host variable links
1. Jump.
### On entry:
1. Shuffle `ENTRY` dummy arguments & jump to common entry point.
1. Complete `VALUE` copying and `INTENT(OUT)` finalization and
reinitialization if these steps are not always done by the caller
(as I think they will be).
1. Optionally compact assumed-shape arguments for contiguity to enable
better SIMD vectorization, if not `TARGET` and not already contiguous.
1. Complete allocation of function result storage, if that has
not been done by the caller.
1. Initialize components of derived type local variables,
including the function result.
Execute the callee, populating the function result or selecting
the subroutine's alternate return.
### On exit:
1. Clean up local scope (finalization, deallocation)
1. Deallocate `VALUE` argument temporaries.
(But don't finalize them; see 7.5.6.3(3)).
1. Replace any assumed-shape argument data that were compacted on
entry for contiguity for SIMD vectorization, if possibly modified
(and never when `INTENT(IN)` or `VALUE`).
1. Identify alternate `RETURN` to caller.
1. Marshal results.
1. Jump
### On return to the caller:
1. Copy actual argument array designator data that was copied into
a temporary back into its original storage (see below).
1. Complete deallocation of actual argument temporaries (not `VALUE`).
1. Reload host-escaping local objects from memory.
1. `GO TO` alternate return, if any.
1. Use the function result in an expression.
1. Eventually, finalize &/or deallocate the function result.
## The messy details
### Copying actual argument values into temporary storage
There are several conditions that require the compiler to generate
code that allocates and populates temporary storage for an actual
argument.
First, actual arguments that are expressions, not designators, obviously
need to be computed and captured into memory in order to be passed
by reference.
This includes parenthesized designators like `(X)` as an important
special case, which are expressions in Fortran.
This case should also include constants.
The dummy argument cannot have `INTENT(OUT)` or `INTENT(IN OUT)`.
Small scalar or elemental `VALUE` arguments may be passed in registers.
Multiple elemental `VALUE` arguments might be packed into SIMD registers.
Actual arguments that are designators, not expressions, must
be copied into temporaries in many situations.
1. Coindexed objects need to be copied into the local image.
(This can get very involved if they contain `ALLOCATABLE`
components, which also need to be copied, along with their
`ALLOCATABLE` components, and may be best implemented with a runtime
library routine working off a description of the type.)
1. Actual arguments associated with dummies with the `VALUE`
attribute need to copied; this could be done on either
side of the call, but there are optimization opportunities
on the caller's side.
1. In non-elemental calls, the values of array sections with
vector-valued subscripts need to be compacted into temporaries.
These actual arguments are not definable, and they are not allowed to
be associated with non-`VALUE` dummy arguments with the attributes
`INTENT(IN)`, `INTENT(IN OUT)`, `ASYNCHRONOUS`, or `VOLATILE`
(15.4.2.4(21)).
1. Non-contiguous arrays being passed to dummy arguments that
must be contiguous due to a `CONTIGUOUS` attribute or not
being assumed-shape/-rank.
This should be a runtime decision, so that actual arguments
that turn out to be contiguous can be passed cheaply.
Actual arguments associated with `INTENT(OUT)` dummies that require
allocation of a temporary don't have to populate it, but they
do have to initialize the storage when a derived type has
component initializations.
Except for `VALUE` and `INTENT(IN)` dummy arguments, the original
contents of local designators that have been compacted into temporaries
could optionally have their `ALLOCATABLE` components invalidated
across the call as an aid to debugging.
Except for `VALUE` and `INTENT(IN)` dummy arguments, the contents of
the temporary storage will be copied back into the actual argument
designator after control returns from the procedure, and it may be necessary
to preserve addresses (or the values of subscripts and cosubscripts
needed to recalculate them) of the actual argument designator, or its
elements, in additional temporary storage if they can't be safely or
quickly recomputed after the call.
### `INTENT(OUT)` preparation
Actual arguments that are associated with `INTENT(OUT)`
dummy arguments are required to be definable.
Such arguments are finalized (as if) on entry to the called
procedure. In particular, in calls to elemental procedures,
the elements of an array are finalized by a scalar or elemental
`FINAL` procedure (7.5.6.3(7)).
Derived type components with initializers are (re)initialized.
The preparation of actual arguments for `INTENT(OUT)` could be
done on either side of the call. If the preparation is
done by the caller, there is an optimization opportunity
in situations where unmodified incoming `INTENT(OUT)` dummy
arguments are being passed onward as outgoing `INTENT(OUT)`
arguments.
### Copying temporary storage back into actual argument designators
Except for `VALUE` and `INTENT(IN)` dummy arguments and array sections
with vector-valued subscripts (15.5.2.4(21)), temporary storage into
which actual argument data were compacted for contiguity before the call
must be redistributed back to its original storage by the caller after
the return.
In conjunction with saved cosubscript values, a standard descriptor
suffices to represent a pointer to the original storage into which the
temporary data should be redistributed.
Note that coindexed objects with `ALLOCATABLE` ultimate components
are required to be associated only with dummy arguments with the
`VALUE` &/or `INTENT(IN)` attributes (15.6.2.4(6)), so there is no
requirement that the local image somehow reallocate remote storage
when copying the data back.
### Host association linkage
Calls to dummy procedures and procedure pointers that resolve to
internal procedures need to pass an additional argument that
addresses on block of storage in the stack frame of the their
host subprogram that was active at the time they were passed as an
actual argument or associated with a procedure pointer.
This is similar to a static link in implementations of programming
languages with nested subprograms, although Fortran only allows
one level of nesting.
The host subprogram objects that are visible to any of their internal
subprograms need to be resident in memory across any calls to them
(direct or not). Any host subprogram object that might be defined
during a call to an internal subprogram needs to be reloaded after
a call or reside permanently in memory.
A simple conservative analysis of the internal subprograms can
identify all of these escaping objects and their definable subset.
The address of the host subprogram storage used to hold the escaping
objects needs to be saved alongside the code address(es) that
represent a procedure pointer.
It also needs to be conveyed alongside the actual argument for a
dummy procedure.
For subprograms that can be called with an implicit interface,
we cannot use a "procedure pointer descriptor" to represent an
actual argument for a dummy procedure -- a Fortran '77 routine
with an `EXTERNAL` dummy argument expects to receive a single
address. Instead, when passing an actual procedure on a call
to a procedure that can be called with an implicit interface,
we will need to use additional arguments to convey the host
storage link addresses.
## Further topics to document
### Target resolution
* polymorphic bindings
* procedure pointers
* dummy procedures
* generic resolution
### Arguments
* Alternate return specifiers
* `%VAL()` and `%REF()`
* Unrestricted specific intrinsic functions as actual arguments
* Check definability of `INTENT(OUT)` and `INTENT(IN OUT)` actuals.
### Naming
* Modules
* Submodules
* Subprograms
* SIMD vs. scalar versions of `ELEMENTAL` procedures
* Mangling explicit interfaces, possibly with versioning
* Unrestricted specific intrinsic functions (and perhaps SIMD variants)
### Other
* SIMD variants of `ELEMENTAL` procedures (& unrestricted specific intrinsics)
* Interoperable procedures
* Multiple code addresses for dummy procedures
* Elemental calls with array arguments