Representation of Fortran function calls¶
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 a 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.
Quick review of terminology¶
A dummy argument is a function or subroutine parameter. It is associated with an effective argument at each call to the procedure.
The shape of an array is a vector containing its extent (size) on each dimension; the rank of an array is the number of its dimensions (i.e., the shape of its shape). The absolute values of the lower and upper bounds of the dimensions of an array are not part of its shape, just their difference (plus 1).
An explicit-shape array has all of its bounds specified; lower bounds default to 1. These can be passed by with a single address and their contents are contiguous.
An assumed-size array is an explicit-shape array with
*as its final dimension, which is the most-significant one in Fortran and whose value does not affect indexed address calculations.
A deferred-shape array (
DIMENSION::A(:)) is a
POINTERtarget data might not be contiguous.
An assumed-shape (not size!) array (
DIMENSION::A(:)) is a dummy argument that is neither
ALLOCATABLE; its lower bounds can be set by the procedure that receives them (defaulting to 1), and its upper bounds are functions of the lower bounds and the extents of dimensions in the shape of the effective argument.
CHARACTER(*)dummy argument takes its length from the effective argument.
CHARACTER(*)result of an external function (C721) has its length determined by its eventual declaration in a calling scope.
DIMENSION::A(..)dummy argument array has an unknown number of dimensions.
POINTERhas a specific derived type or some extension of that type. An unlimited polymorphic
CLASS(*)object can have any intrinsic or derived type.
BIND(C)procedures are written in C or callable from C.
Referenced procedures may or may not have declared interfaces available to their call sites.
Procedures with some post-Fortran ‘77 features require an explicit interface to be called (188.8.131.52) or even passed (4.3.4(5)):
use of argument keywords in a call
procedures that are
procedures that are required to be
PUREdue to the context of the call (specification expression,
dummy arguments with these attributes:
VOLATILE, and, as a consequence of limitations on its use,
INTENT(), however, does not require an explicit interface
dummy arguments that are coarrays
dummy arguments that are assumed-shape or assumed-rank arrays
dummy arguments with parameterized derived types
dummy arguments that are polymorphic
function result that is an array
function result that is
CHARACTERfunction result whose length is neither constant nor assumed
derived type function result with
LENtype parameter value that is not constant (note that result derived type parameters cannot be assumed (C795))
Module procedures, internal procedures, procedure pointers,
type-bound procedures, and recursive references by a procedure to itself
always have explicit interfaces.
(Consequently, they cannot be assumed-length
CHARACTER(*) functions are prohibited from
Other uses of procedures besides calls may also require explicit interfaces, such as procedure pointer assignment, type-bound procedure bindings, &c.
Note that non-parameterized monomorphic derived type arguments do
not by themselves require the use of an explicit interface.
However, dummy arguments with any derived type parameters do
require an explicit interface, even if they are all
184.108.40.206(2) explicitly allows an assumed-length
to be passed as an actual argument to an explicit-length dummy;
this has implications for calls to character-valued dummy functions
and function pointers.
(In the scopes that reference
CHARACTER functions, they must have
visible definitions with explicit result lengths.)
In the absence of any characteristic or context that requires an
explicit interface (see above), an external function or subroutine (R503)
ENTRY (R1541) can be called directly or indirectly via its implicit interface.
Each of the arguments can be passed as a simple address, including
Procedures that can be called via an implicit interface can
undergo more thorough checking
by semantics when an explicit interface for them exists, but they must be
compiled as if all calls to them were through the implicit interface.
This note will mention special handling for procedures that are exposed
to the possibility of being called with an implicit interface as F77ish procedures
below; this is of course not standard terminology.
Internal and module subprograms that are ever passed as arguments &/or assigned as targets of procedure pointers may be F77ish.
Every F77ish procedure can and must be distiguished at compilation time. Such procedures should respect the external naming conventions (when external) and any legacy ABI used for Fortran ‘77 programs on the target architecture, so that portable libraries can be compiled and used by distinct implementations (and their versions) of Fortran.
Note that F77ish functions 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: these F77sh procedures that do not require the use of an explicit
interface and that can possibly be referenced, directly or indirectly,
with implicit interfaces are limited to argument lists that comprise
only the addresses of effective arguments and the length of a
CHARACTER function result
(when there is one), and they can return only scalar values with constant
type parameter values.
None of their arguments or results need be (or can be) implemented
and any internal procedures passed to them as arguments must be
simple addresses of non-internal subprograms or trampolines for
Note that the
INTENT attribute does not, by itself,
require the use of explicit interface; neither does the use of a dummy
procedure (implicit or explicit in their interfaces).
So the analyis of calls to F77ish procedures must allow for the
invisible use of
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:¶
Compute &/or copy into temporary storage the values of some effective argument expressions and designators (see below).
Create and populate descriptors for arguments that use them (see below).
Possibly allocate function result storage, when its size can be known by all callers; function results that are neither
ALLOCATABLEmust have explicit shapes (C816).
Create and populate a descriptor for the function result, if it needs one (deferred-shape/-length
ALLOCATABLE, derived type with non-constant length parameters, &c.).
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).
Resolve the target procedure’s polymorphic binding, if any.
Marshal effective argument addresses (or values for
%VAL()and some discretionary
VALUEarguments) into registers.
CHARACTERargument lengths in additional value arguments for
CHARACTEReffective arguments not passed via descriptors. These lengths must be 64-bit integers.
Marshal an extra argument for the length of a
CHARACTERfunction result if the function is F77ish.
Marshal an extra argument for the function result’s descriptor, if it needs one.
Set the “host instance” (static link) register when calling an internal procedure from its host or another internal procedure, a procedure pointer, or dummy procedure (when it has a descriptor).
For subprograms with alternate
ENTRYdummy arguments set a compiler-generated variable to identify the alternate entry point, and jump to the common entry point for common processing and a
switch()to the statement after the
CHARACTERargument &/or assumed-length result length values.
VALUEcopying if this step will not always be done by the caller (as I think it should be).
Finalize &/or re-initialize
INTENT(OUT)non-pointer effective arguments (see below).
For interoperable procedures called from C: compact discontiguous dummy argument values when necessary (
CONTIGUOUS&/or explicit-shape/assumed-size arrays of assumed-length
Optionally compact assumed-shape arguments for contiguity on one or more leading dimensions to improve SIMD vectorization, if not
TARGETand not already sufficiently contiguous. (PGI does this in the caller, whether the callee needs it or not.)
Complete allocation of function result storage, if that has not been done by the caller.
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.
Clean up local scope (finalization, deallocation)
VALUEargument temporaries. (But don’t finalize them; see 220.127.116.11(3)).
Replace any assumed-shape argument data that were compacted on entry for contiguity when the data were possibly modified across the call (never when
On return to the caller:¶
Save the result registers, if any.
Copy effective argument array designator data that was copied into a temporary back into its original storage (see below).
Complete deallocation of effective argument temporaries (not
Reload definable host-escaping local objects from memory, if they were saved to memory by the host before the call.
GO TOalternate return, if any.
Use the function result in an expression.
Eventually, finalize &/or deallocate the function result.
(I’ve omitted some obvious steps, like preserving/restoring callee-saved registers on entry/exit, dealing with caller-saved registers before/after calls, and architecture-dependent ABI requirements.)
The messy details¶
Copying effective 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, effective arguments that are expressions, not designators, obviously
need to be computed and captured into memory in order to be passed
This includes parenthesized designators like
(X), which are
expressions in Fortran, as an important special case.
(This case also technically includes unparenthesized constants,
but those are better implemented by passing addresses in read-only
The dummy argument cannot be known to have
Small scalar or elemental
VALUE arguments may be passed in registers,
as should arguments wrapped in the legacy VMS
VALUE arguments might be packed into SIMD registers.
Effective arguments that are designators, not expressions, must also be copied into temporaries in the following situations.
Coindexed objects need to be copied into the local image. This can get very involved if they contain
ALLOCATABLEcomponents, which also need to be copied, along with their
ALLOCATABLEcomponents, and may be best implemented with a runtime library routine working off a description of the type.
Effective arguments associated with dummies with the
VALUEattribute need to be copied; this can be done on either side of the call, but there are optimization opportunities available when the caller’s side bears the responsibility.
In non-elemental calls, the values of array sections with vector-valued subscripts need to be gathered into temporaries. These effective arguments are not definable, and they are not allowed to be associated with non-
VALUEdummy arguments with the attributes
INTENT()can’t always be checked.
Non-simply-contiguous (9.5.4) arrays being passed to non-
POINTERdummy arguments that must be contiguous (due to a
CONTIGUOUSattribute, or not being assumed-shape or assumed-rank; this is always the case for F77ish procedures). This should be a runtime decision, so that effective arguments that turn out to be contiguous can be passed cheaply. This rule does not apply to coarray dummies, whose effective arguments are required to be simply contiguous when this rule would otherwise force the use of a temporary (18.104.22.168); neither does it apply to
VOLATILEeffective arguments, which are disallowed when copies would be necessary (C1538 - C1540). Only temporaries created by this contiguity requirement are candidates for being copied back to the original variable after the call (see below).
Fortran requires (18.3.6(5)) that calls to interoperable procedures with dummy argument arrays with contiguity requirements handle the compaction of discontiguous data in the Fortran callee, at least when called from C. And discontiguous data must be compacted on the caller’s side when passed from Fortran to C (18.3.6(6)).
We could perform all argument compaction (discretionary or required) in the callee, but there are many cases where the compiler knows that the effective argument data are contiguous when compiling the caller (a temporary is needed for other reasons, or the effective argument is simply contiguous) and a run-time test for discontiguity in the callee can be avoided by using a caller-compaction convention when we have the freedom to choose.
While we are unlikely to want to needlessly use a temporary for
an effective argument that does not require one for any of these
reasons above, we are specifically disallowed from doing so
by the standard in cases where pointers to the original target
data are required to be valid across the call (22.214.171.124(9-10)).
In particular, compaction of assumed-shape arrays for discretionary
contiguity on the leading dimension to ease SIMD vectorization
cannot be done safely for
TARGET dummies without
Effective arguments associated with known
INTENT(OUT) dummies that
require allocation of a temporary – and this can only be for reasons of
contiguity – don’t have to populate it, but they do have to perform
minimal initialization of any
ALLOCATABLE components so that
the runtime doesn’t crash when the callee finalizes and deallocates
ALLOCATABLE coarrays are prohibited from being affected by
Note that calls to implicit interfaces must conservatively allow
for the use of
INTENT(OUT) by the callee.
VALUE and known
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.
VALUE and known
INTENT(IN) dummy arguments, the contents of
the temporary storage will be copied back into the effective 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 effective argument designator, or its
elements, in additional temporary storage if they can’t be safely or
quickly recomputed after the call.
Effective arguments that are associated with
dummy arguments are required to be definable.
This cannot always be checked, as the use of
does not by itself mandate the use of an explicit interface.
INTENT(OUT) 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 (126.96.36.199(7)).
Derived type components that are
ALLOCATABLE are finalized
and deallocated; they are prohibited from being coarrays.
Components with initializers are (re)initialized.
The preparation of effective 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
arguments whose types lack
FINAL procedures are being passed
onward as outgoing
Arguments and function results requiring descriptors¶
Dummy arguments are represented with the addresses of new descriptors when they have any of the following characteristics:
assumed-shape array (
assumed-rank array (
parameterized derived type with assumed
coarray dummy argument
INTENT(IN) POINTERargument (188.8.131.52, C.10.4)
ALLOCATABLE and other
POINTER arguments can be passed by simple
Non-F77ish procedures use descriptors to represent two further kinds of dummy arguments:
F77ish procedures use other means to convey character length and host instance links (respectively) for these arguments.
Function results are described by the caller & callee in a caller-supplied descriptor when they have any of the following characteristics, some which necessitate an explicit interface:
deferred-shape array (so
derived type with any non-constant
LENparameter (C795 prohibit assumed lengths)
procedure pointer result (when the interface must be explicit)
Storage for a function call’s result is allocated by the caller when
possible: the result is neither
the shape is scalar or explicit, and the type has
that are constant expressions.
In other words, the result doesn’t require the use of a descriptor
but can’t be returned in registers.
This allows a function result to be written directly into a local
variable or temporary when it is safe to treat the variable as if
it were an additional
CHARACTER results, assumed or explicit, is always
allocated by the caller, and the length is always passed so that
an assumed-length external function will work when eventually
called from a scope that declares the length that it will use
Note that the lower bounds of the dimensions of non-
ALLOCATABLE dummy argument arrays are determined by the
callee, not the caller.
(A Fortran pitfall: declaring
A(0:9), passing it to a dummy
D(:), and assuming that
LBOUND(D,1) will be zero
in the callee.)
If the declaration of an assumed-shape dummy argument array
contains an explicit lower bound expression (R819), its value
needs to be computed by the callee;
it may be captured and saved in the incoming descriptor
as long as we assume that argument descriptors can be modified
Callers should fill in all of the fields of outgoing
descriptors with the assumption that the callee will use 1 for
lower bound values, and callees can rely on them being 1 if
Copying temporary storage back into argument designators¶
VALUE and known
INTENT(IN) dummy arguments and array sections
with vector-valued subscripts (184.108.40.206(21)), temporary storage into
which effective argument data were compacted for contiguity before the call
must be redistributed back to its original storage by the caller after
In conjunction with saved cosubscript values, a standard descriptor would suffice to represent a pointer to the original storage into which the temporary data should be redistributed; the descriptor need not be fully populated with type information.
Note that coindexed objects with
ALLOCATABLE ultimate components
are required to be associated only with dummy arguments with the
INTENT(IN) attributes (220.127.116.11(6)), so there is no
requirement that the local image somehow reallocate remote storage
when copying the data back.
Calls to the type-bound procedures of monomorphic types are
resolved at compilation time, as are calls to
The resolution of calls to overridable type-bound procedures of
polymorphic types must be completed at execution (generic resolution
of type-bound procedure bindings from effective argument types, kinds,
and ranks is always a compilation-time task (15.5.6, C.10.6)).
Each derived type that declares or inherits any overridable type-bound procedure bindings must correspond to a static constant table of code addresses (or, more likely, a static constant type description containing or pointing to such a table, along with information used by the runtime support library for initialization, copying, finalization, and I/O of type instances). Each overridable type-bound procedure in the type corresponds to an index into this table.
External subroutines and functions (R503) and
ENTRY points (R1541)
BIND(C) (R808) have linker-visible names that are either explicitly
specified in the program or determined by straightforward rules.
The names of other F77ish external procedures should respect the conventions
of the target architecture for legacy Fortran ‘77 programs; this is typically
In other cases, however, we have fewer constraints on external naming, as well as some additional requirements and goals.
Module procedures need to be distinguished by the name of their module and (when they have one) the submodule where their interface was defined. Note that submodule names are distinct in their modules, not hierarchical, so at most two levels of qualification are needed.
ELEMENTAL functions (15.8) must use distinct names for any alternate
entry points used for packed SIMD arguments of various widths if we support
calls to these functions in SIMD parallel contexts.
There are already conventions for these names in
The names of non-F77ish external procedures should be distinguished as such so that incorrect attempts to call or pass them with an implicit interface will fail to resolve at link time. Fortran 2018 explicitly enables us to do this with a correction to Fortran 2003 in 4.3.4(5).
Last, there must be reasonably permanent naming conventions used by the F18 runtime library for those unrestricted specific intrinsic functions (table 16.2 in 16.8) and extensions that can be passed as arguments.
In these cases where external naming is at the discretion of the implementation, we should use names that are not in the C language user namespace, begin with something that identifies the current incompatible version of F18, the module, the submodule, and elemental SIMD width, and are followed by the external name. The parts of the external name can be separated by some character that is acceptable for use in LLVM IR and assembly language but not in user Fortran or C code, or by switching case (so long as there’s a way to cope with extension names that don’t begin with letters).
In particular, the period (
.) seems safe to use as a separator character,
Fa. prefix can serve to isolate these discretionary names from
other uses and to identify the earliest link-compatible version.
Fa.mod.submod.foo, and (for an external
subprogram that requires an explicit interface)
When the ABI changes in the future in an incompatible way, the
initial prefix becomes
Summary of checks to be enforced in semantics analysis¶
INTENT(OUT)argument shall not be associated with an object that is or has an allocatable coarray.
INTENT(OUT)argument shall not have
(C863) The argument cannot be assumed-size, a coarray, or have a coarray ultimate component.
(C864) The argument cannot be
INTENT(IN OUT), or
(C865) If the procedure is
BIND(C), the argument cannot be
15.5.1 procedure references:
(C1533) can’t pass non-intrinsic
(C1536) alternate return labels must be in the inclusive scope
(C1537) coindexed argument cannot have a
18.104.22.168 requirements for non-
(2) dummy must be monomorphic for coindexed polymorphic actual
(2) dummy must be polymorphic for assumed-size polymorphic actual
(2) dummy cannot be
TYPE(*)if effective is PDT or has TBPs or
(4) character length of effective cannot be less than dummy
(6) coindexed effective with
ALLOCATABLEultimate component requires
(13) a coindexed scalar effective requires a scalar dummy
(14) a non-conindexed scalar effective usually requires a scalar dummy, but there are some exceptions that allow elements of storage sequences to be passed and treated like explicit-shape or assumed-size arrays (see 22.214.171.124)
(16) array rank agreement
INTENT(IN OUT)dummies require definable actuals
(21) array sections with vector subscripts can’t be passed to definable dummies (
VOLATILEattributes must match when dummy has a coarray ultimate component
(C1538 - C1540) checks for
126.96.36.199 requirements for
POINTER arguments when both
the dummy and effective arguments have the same attributes:
(2) both or neither can be polymorphic
(2) both are unlimited polymorphic or both have the same declared type
(3) rank compatibility
(4) effective argument must have deferred the same type parameters as the dummy
ALLOCATABLE dummy arguments:
(2) effective must be
(3) corank must match
(4) coindexed effective requires
INTENT(IN OUT)dummies require definable actuals
POINTER dummy arguments:
CONTIGUOUSdummy requires simply contiguous actual
(C1542) effective argument cannot be coindexed unless procedure is intrinsic
(2) effective argument must be
POINTERunless dummy is
INTENT(IN)and effective could be the right-hand side of a pointer assignment statement
188.8.131.52 corray dummy arguments:
(1) effective argument must be coarray
VOLATILEattributes must match
(2) explicitly or implicitly contiguous dummy array requires a simply contiguous actual
184.108.40.206 dummy procedures:
(1) explicit dummy procedure interface must have same characteristics as actual
(5) dummy procedure
POINTERrequirements on effective arguments
220.127.116.11 procedure definitions:
NON_RECURSIVEprocedures cannot recurse.
CHARACTER(*)functions cannot be declared as
ELEMENTAL, or `PURE’ (C723), and cannot be called recursively (18.104.22.168(3)).
(C823) A function result cannot be a coarray or contain a coarray ultimate component.
PURE requirements (15.7): C1583 - C1599.
These also apply to
ELEMENTAL procedures that are not
ELEMENTAL requirements (15.8.1): C15100-C15103,
and C1533 (can’t pass as effective argument unless intrinsic)
For interoperable procedures and interfaces (18.3.6):
C1552 - C1559
function result is scalar and of interoperable type (C1553, 18.3.1-3)
VALUEarguments are scalar and of interoperable type
POINTERdummies cannot be
CONTIGUOUS(18.3.6 paragraph 2(5))
assumed-type dummies cannot be
POINTER, assumed-shape, or assumed-rank (18.3.6 paragraph 2 (5))
CHARACTERdummies that are
POINTERmust be deferred-length
Further topics to document¶
Alternate return specifiers
%DESCR()legacy VMS interoperability extensions
Unrestricted specific intrinsic functions as effective arguments
SIMD variants of
ELEMENTALprocedures (& unrestricted specific intrinsics)
Elemental subroutine calls with array arguments