Sather - A Language Manual - Interfacing with Fortran

[back] [Abstract] [Copyright Notice] [Contents] [next]

# Sather - A Language Manual - Chapter 13
Interfacing with Fortran

Providing a type-safe Sather interface to Fortran 77 is desirable for several reasons. There is a large body of well debugged and well tested high performance Fortran source code for various kinds of numerical computations. Many vendors provide versions of low level numerical Fortran libraries tuned for particular hardware platforms. Fortran 77 BLAS have become a de facto standard for the elementary vector and matrix operations. The external Fortran interface provides a standard mechanism for Fortran procedures and data to be accessed from Sather and vice versa. It enables a Sather programmer to exploit the wealth of available numerical software in a type safe and portable manner.

Several important issues need to be resolved to provide interoperability between Sather and Fortran. The issues are:

- name binding

- datatype mapping

- parameter passing

Overview, section 13.1 introduces the Sather/Fortran interface and provides a few illustrative examples. Name Binding, section 13.2 talks about binding Sather entities to corresponding Fortran entities. Section Datatype Mapping, section 13.3 provides a mapping of "basic" Sather types to Fortran types. Section Parameter Passing, section 13.4 explains how arguments in a Sather call are passed to a Fortran procedure or function that implements the feature. Finally, section Portability Issues, section 13.5 talks about various portability issues.

## 13.1 Overview

Sather 1.1 provides an interface to a superset of Fortran 77 (ANSI X3.9-1978). The interoperability with Fortran code is achieved with the help of external Fortran classes. External Fortran classes are used to implement a strongly typed bidirectional Sather/Fortran interface. The extended library provides a set of built-in classes corresponding to all Fortran 77 types. Signatures of all inter-language calls must contain only these built-in classes as argument or return types.

### 13.1.1 External Fortran Call Example

The keywords `'external'` and `'FORTRAN'` preceding `'class'` indicate that some class features may be implemented externally in Fortran and some other features are compiled in a way that makes it possible to call them from Fortran. An example of a simple call to a Fortran function is given below

    external FORTRAN class FOO is
       foo(a:F_INTEGER,b:F_INTEGER):F_INTEGER;
       -- a feature with a missing body is implemented externally
       -- in Fortran.
       -- Fortran definition:
       -- INTEGER FUNCTION foo(A,B)
       -- INTEGER A
       -- INTEGER B
       -- ...
    end;

    -- a call to an externally defined Fortran function
    i:F_INTEGER := FOO::foo(#F_INTEGER(1), #F_INTEGER(2));
    -- #F_INTEGER(1) creates a variable of Fortran type F_INTEGER and
    --  initializes it to 1,
    -- #F_INTEGER(2) does a similar job,but initializes a new variable to 2

`F_INTEGER` is a built-in type representing Fortran integers. A full list of builtin-in Fortran types will be given in Datatype Mapping, section 13.3. Standard libraries provide a set of constructors and conversion routines for conversion from Sather to Fortran types and vice versa. The definition of feature `'foo'` in external class FOO looks similar to abstract signatures in abstract classes. The implementation of external classes methods without bodies is assumed to be given in a corresponding language (Fortran in the case of 'foo'.) Such abstract signatures specify the interface from Sather code to Fortran code.

### 13.1.2 Overall Organization

External Fortran classes are used to provide both Sather/Fortran and Fortran/Sather interfaces. External Fortran classes can contain methods of two kinds: bodyless routines indicating externally implemented features and methods with code bodies some of which could be called from Fortran code. External Fortran classes cannot be instantiated and exist only to provide a bidirectional interface from Sather to Fortran.

Only routines may have no body in the external Fortran classes (not iterators). Bodyess routines specify the interface for Sather code (both in external and "regular" Sather classes) to call Fortran code. They have Sather signatures corresponding to the Fortran functions and subroutines implementing these features. Calls to such routines are compiled using the Fortran style name binding and parameter passing convention. The full correspondence between Fortran 77 types and Sather built-in Fortran classes is given in Datatype Mapping, section 13.3.

Methods with bodies in external Fortran classes serve a dual purpose. Methods whose arguments and return types are a combination of Sather and external Fortran types are merely helper routines and iterators whose semantics is the same as that of regular routines and iterators. They could be called from any Sather or external classes and such calls support the Sather name binding and parameter passing convention. Code for such methods can contain all sorts of calls without restrictions.

If all argument types and a return type, if any, in a routine with a body are built-in Fortran types (e.g. F_INTEGER, F_REAL, etc.) , such routines are meant to be callable from Fortran. They are compiled using the Fortran name binding and parameter passing convention. In fact, they could be freely substituted for Fortran 77 subroutines and functions that perform the same functions. Such routines could be also called from Sather code, but these calls will also support the Fortran parameter passing convention which is often less efficient relative to regular Sather calls. There are no restrictions on the implementation of these function: they can freely use internally any methods implemented either in Sather or Fortran. Routines which are meant to be called from Fortran cannot be overloaded.

In the diagram, arrows indicate the direction of calls. For example, an arrow connecting Fortran classes with bodyless routines in External classes indicate calls in the regular Sather code to abstract routines in the external Fortran classes. The type of the arrow demonstrates that such calls are compiled using the Fortran style call name binding and parameter passing convention. On the other hand, calls from routines with bodies in the external Fortran classes into regular Sather classes are represented by a solid arrow which denotes the Sather call name binding and parameter passing convention.

    -- This is a Fortran definition for FOO
    INTEGER FUNCTION FOO(I,A,C)
    INTEGER I
    REAL A
    CHARACTER C
    ....
    END

    external FORTRAN class FOO is
       -- this routine is implemented externally in Fortran and could
       -- be called in Sather like this: tmp::=FOO::foo(i,a,c)
       foo(i:F_INTEGER,a:F_REAL,c:F_CHARACTER):F_INTEGER;

       -- this routine could be called from both Sather and Fortran
       -- all calls to bar (either from Sather or Fortran) use the
       -- Fortran 77 parameter passing convention
       bar(i:F_INTEGER,a:F_REAL) is
          ...
       end;

       -- this routine can only be called in Sather since
       -- argument size has a Sather type
       helper(arr:F_ARRAY{F_INTEGER}, size:INT) is
          ...
          t::=foo(i,a,c);  --call uses Fortran parameter passing convention
          bar(t,a);        --Fortran convention, but implemented in Sather
       end;
    end;

In this example, a Fortran function implementing 'foo' is called in Sather code as if it were a regular Sather routine: FOO::foo(i,a,c). However, the call is generated using the Fortran name binding and parameter passing convention. Calls to 'bar' are compiled in a similar fashion; however, it could be called from both Sather or external Fortran code. Finally, 'helper' has both Sather and external Fortran types as arguments and therefore could be called from Sather code only.

Points to note

- External Fortran class routines without bodies (abstract signatures) provide Sather/Fortran interface.

- Routines with bodies could be called from Fortran if and only if their signatures contain only built-in Fortran types. Such routines can be also called from Sather. However, regardless of the way they are called, they always support the Fortran style name binding and parameter passing convention.

- Methods with bodies whose signatures have non-Fortran types are regular Sather methods. They could be used as helper methods for the interface classes. They always support Sather style name binding and parameter passing.

## 13.2 Name Binding

Symbols for Sather calls to Fortran code need to be generated in exactly the same way as a Fortran 77 compile would. This is also necessary for the names of routines intended to be called from Fortran. This is difficult to ensure in a portable way since neither Sather nor Fortran 77 language specification prescribes any symbol binding convention and the name mangling strategy is usually very sensitive to particular Fortran platforms. Sather 1.1 attempts to solve the name biding problem in an easy to use, but sufficiently general manner.

### 13.2.1 Difficulties

Various naming issues have to be resolved to provide seamless platform independent interoperability between Sather and Fortran. Neither Sather nor Fortran specifies a way to mangle symbols generated for the linking stage. Moreover, various Fortran compilers adopt vastly different naming strategies and, in general, it is impossible to link together object files generated by different Fortran compilers. Unfortunately, this is the case even for relatively mainstream platforms: for instance, AT&T f77 compiler name mangling is very different from that of Sun's f77 compiler.

This is an incomplete list of various Fortran 77 naming practices

- long names may be truncated at various lengths

- Fortran names may have one (most common) or two underscores appended

- Fortran names are usually forced to lower case

- external names (external procedures and common blocks) may be mangled differently from internal names (various number of trailing underscores, etc.)

The Sather symbols may be generated using quite different naming conventions. For instance, the ICSI Sather 1.1 compiler generates symbols for Sather routine and iterator names by concatenating a class name (including class parameters) with a routine name, truncating the resulting name to a length specified at the compiler configuration/installation step and appending a number at the end to make the name unique. Other Sather implementations are free to choose any name binding convention.

The set of problems we have to deal with is the same set of problems that needs to be resolved to provide interoperability between Fortran and such an "old" language as C. To this day, there is no standard or even a concrete proposal to resolve F77/C, HPF/C or F95/C name binding issues in a platform independent fashion.

The Sather 1.1 implementation deals with the naming issues in a more fundamental fashion, in some respects, than any of the mentioned external interface proposals.

### 13.2.2 Implementation

The name mangling strategy for external Fortran names generated by Sather is set at the compiler configuration time. Thus, to move a mixed language program or library from one platform to another, it is only necessary to reconfigure the Sather compiler at the compiler installation time to inform it about the naming convention of the Fortran compiler on the new platform. All user and library code will continue working as is.

There are at least three potential ways to insure the portability of name binding. The simplest (conceptually, not practically!) way is to keep a list of all known Fortran compilers and used name mangling conventions. The Sather compiler should be able to implement any of the possible name binding strategies. This solution was adopted (not implemented!) by the HPFF proposal to provide HPPF/C interoperability. Problems with this approach:

- works only with the existing compilers for other languages. A new compiler cannot be support unless major modifications to the existing tools are performed.

- adds lots of complexity to the Sather compiler as it must know many things about common Fortran compilers

- moving to a new Fortran platform may potentially require large modifications to the Sather compiler internals as the mangling decisions for special cases are hardcoded in the Sather compiler

Another solution that tries to simplify Sather compiler complexity is to add a "Fortran name bind" directive to the Sather language. This directive would specify an actual binding name for each Fortran routine meant to be called from Sather and each Sather routine callable from Fortran. F95/C interoperability proposal partially adopts this approach. This solution, however tedious it may be for the user, may be unavoidable for Fortran to interface other languages since Fortran names are always converted to lowercase and to call an external routine whose name in the symbol table has at least a single uppercase letter a new language construct needs to be added to Fortran. This particular problem, however, may be avoided for Sather. Nevertheless, there are some serious problems with this approach:

- the burden is entirely on the user's shoulders. He/she needs to be aware about too many low-level name binding details

- the "name bind" directive pollutes the code with things that are irrelevant for the program semantics

- it is a pain to port a program to a different Fortran platform: name binding will need to change accordingly

Finally, a completely general solution is to provide a Sather compiler at configuration time with a stand alone function that would take the Sather name as an input and generate a biding Fortran name as output that conforms to all conventions of the current Fortran platform. A library of such functions for most common platforms could be distributed with the compiler, and to port the Sather compiler to an exotic Fortran platform, only a single function will need to be written (or modified given a valid Fortran platform with a similar functionality.) This approach was considered as superior in the F95/C Interoperability Technical Report (ISO/IEC JTC1/SC22/WG5 N1147), but it was not accepted because of the F95 compiler implementation difficulties.

Sather 1.1 tries to shield the user completely from the horrors of low-level mangling details. It adopts the third and most general strategy. In addition, it also provides simple hooks for most common Fortran mangling conventions.

Most Fortran compilers simply append an underscore as a prefix or suffix to the textual name (modulo necessary truncation) . The same behavior for external names could be achieved by setting either one or both configuration variables in the CONFIG file for a particular platform at installation time:

    FORTRAN_APPEND_UNDERSCORE: true;
    FORTRAN_PREFIX_UNDERSCORE: false;

In this example, the Fortran binding name is generated from the routine name used in the external Fortran class by appending '_'.

If this is not sufficient, a general Fortran name mangling function can be specified at installation time:

    FORTRAN_BIND_FUNC:  true;

When FORTRAN_BIND_FUNC configuration variable is set to true, a general name binding function BIND_FORTRAN::bind_name(name:STR):STR is invoked whenever Fortran symbols are generated. It, in turn, can call any user supplied mangling function capturing the peculiarity of a particular Fortran platform. BIND_FORTRAN class contains most common binding functions. To port the system to an exotic Fortran environment, a single name binding routine needs to be added to BIND_FORTRAN.

Class BIND_FORTRAN resides in the Fortran library. The following Fortran name binding function simply appends an underscore to the textual name:

    class BIND_FORTRAN is
       -- contains various functions binding Fortran names for exotic
       -- architectures. "bind_name" should always call the appropriate
       -- function and FORTRAN_FUNC_BIND in CONFIG should be set to true
       bind_name(name:STR):STR is
          res:STR;
          -- various Fortran mangling routines should be plugged in here
          res := append_underscore(name);
          return res;
       end;

       append_underscore(s:STR):STR is
          return s + "_";
       end;
    end;

## 13.3 Datatype Mapping

The extended Sather 1.1 library provides a set of built-in classes interfacing to Fortran. These types are "binary" compatible with their Fortran 77 counterparts. Only these built-in classes may be used in signatures of routines implemented in Fortran or Sather routines called from Fortran. Fortran scalar types can be used alone or as parametrizations for built-in Fortran array classes. Sather also provides a convenient way for packaging Sather routines and passing them to Fortran functions or subroutines that expect externally defined subroutines as arguments.

Built-in Scalar Types

Fortran 77 -> Sather class
  Features

`integer` -> `F_INTEGER`
  binary compatible with Fortran 77 integers and can be used whenever Fortran integer type is expected. Supports arithmetic and relational operations, construction from and convention to INT

`real` -> `F_REAL`
  represents Fortran 77 reals and can be used whenever Fortran real type is expected. Supports arithmetic and relational operations, construction from and convention to FLT

`logical` -> `F_LOGICAL`
  binary compatible with Fortran 77 logical. Supports logical operations and constructors from Sather BOOL type.

`double precision` -> `F_DOUBLE`
  binary compatible with Fortran 77 double precision type. Supports a set of features simialr to F_REAL

`complex` -> `F_COMPLEX`
  binary compatible with Fortran 77 complex type. Supports arithmetic operations and creation from Sather CPX type (although the binary representation is quite different from CPX)

`double complex` -> `F_DOUBLE_COMPLEX`
  binary compatible with Fortran 77 double complex type. Supports a set of features similar to F_COMPLEX, but uses double precision arithmetic.

`character,character*1` -> `F_CHARACTER`
  binary compatible with both Fortran 77 character and character*1 types. As an optimizations, inside Sather space it is represented by a single byte and is, therefore, more efficient than corresponding Fortran 77 types.

`character*n` -> `F_STRING`
  binary compatible with Fortran 77 character*n type (including character*1). Intra Sather calls are slightly more efficient than corresponding Fortran/Fortran, Sather/Fortran or Fortran/Sather calls.

Array Types

Fortran 77 -> Sather Types
  Features

Various array types -> `F_ARRAYn{T<$F_SCALAR}` where n = 1,2,..
  Can be parametrized by any scalar Fortran types, binary compatible with the corresponding Fortran 77 arrays: use the same layout. Can be constructed using Sather arrays, matrix and vector classes. arr:F_ARRAY{F_INTEGER} corresponds to INTEGER arrr(*) in Fortran.

Fortran Routine and Exception Handler Types

Fortran 77 -> Sather Type
  Features

External subroutines passed as arguments -> `F_ROUT{}`
  Used to bind Fortran routines, strongly type checked. Can be passed as arguments to external Fortran routines that expect externally defined subroutines as parameters.

Alternate returns (exception handling) -> `F_HANDLER`
  Implements Fortran exception handling in Sather. Can be passed as an arguments to Fortran subroutines with alternate returns (Fortran's way to handle exceptional or abnormal conditions.)

There is also a facility for Sather to provide exception handlers for Fortran subroutines with alternate returns (Fortran's way to handle exceptional or abnormal conditions).

### 13.3.1 Scalar Types

There are eight built-in scalar types: F_INTEGER, F_REAL, F_LOGICAL, F_DOUBLE, F_COMPLEX, F_DOUBLE_COMPLEX, F_CHARACTER, and F_STRING. They correspond to Fortran 77 types as shown in the table. All scalar Fortran types are subtypes of $F_SCALAR ($F_SCALAR is used as a bound for array parametrizations to ensure that arrays are parameterized with scalar types only).

It is important to distinguish between external Fortran interface types and "regular" Sather types with similar semantics. For example, Sather type INT is different from Fortran F_INTEGER, although both abstract the meaning of integers. There is no sub- or super-typing relationship between INT and F_INTEGER and these types cannot be used interchangeably. No assumption could be made about the relative amounts of memory the Sather and Fortran types need. This is defined differently by Sather and Fortran 77 language specifications. For instance, the only relevant Fortran 77 rule guarantees that integer, logical, and real Fortran types occupy the same amount of memory, and double precision and complex types occupy twice as much (the language does not specify the absolute amounts). Sather, on the other hand, does not specifically support these assumptions.

F_INTEGER

F_INTEGER is a Sather 1.1 class representing Fortran 77 integer type. It can be used whenever a Fortran 77 integer is expected: calls to routines implemented in Fortran, Fortran array parametrizations, etc. The Sather 1.1 library defines the following features for F_INTEGER

    class F_INTEGER is
       create(x:INT):F_INTEGER is ...  -- construct from INT
       int:INT is ...                  -- INT version of self
       str:STR is ...                  -- string representation
       zero:SAME is ...                -- zero and
       nil:SAME is ...                 -- nil values
       is_nil:BOOL is ...              -- true if self is nil
       plus(i:SAME):SAME is ...
       minus(i:SAME):SAME is ...
       times(i:SAME):SAME is ...
       div(i:SAME):SAME is ...
       is_eq(i:SAME):BOOL is ...
       is_lt(i:SAME):BOOL is ...
    end;

F_INTEGER could be created using a Sather INT type. An existing F_INTEGER could also yield a corresponding Sather INT value. Although the intended use for F_INTEGER variables is to be passed as arguments to and from external Fortran routines, some simple operations on F_INTEGER variables are built-in and could be performed in Sather directly without going through Fortran. Such operations are the regular arithmetic operations (+ -* /) and logical operations. Syntactic sugar and operator precedence rules are same as those for Sather types.

This example uses an external function defined in Fortran to implement a factorial function missing in the F_INTEGER interface:

    *     A Fortran function that implements factorial of N
          INTEGER FUNCTION FACTORIAL(N)
          INTEGER N
          FACTORIAL = 1
          DO 10, I=1,N
             FACTORIAL = FACTORIAL * I
     10   CONTINUE
          END

    external FORTRAN class USEFUL_FUNCTIONS is
       factorial(i:F_INTEGER):F_INTEGER;
       -- a function implemented in Fortran that returns factorial of i
    end;

    class MAIN is
       main is
          i:F_INTEGER := #(4);
          a:F_INTEGER := USEFUL_FUNCTIONS::factorial(i);
          #OUT + "This " + a.str + " should be 24\n";
       end;
    end;

F_REAL

F_INTEGER, F_REAL represents Fortran 77 real type.

    class F_REAL is
       create(x:FLT):F_REAL is ...  -- construct from FLT
       flt:INT is ...               -- FLT version of self
       str:STR is ...               -- string representation
       zero:SAME is ...             -- zero and
       nil:SAME is ...              -- nil values
       is_nil:BOOL is ...           -- true if self is nil
       plus(i:SAME):SAME is ...
       minus(i:SAME):SAME is ...
       times(i:SAME):SAME is ...
       div(i:SAME):SAME is ...
       is_eq(i:SAME):BOOL is ...
       is_lt(i:SAME):BOOL is ...
    end;

Sather syntactic sugar for arithmetic and relational operations and operator precedence rules apply to F_REAL. Now, we can extend USEFUL_FUNCTIONS class with a power routine for F_REAL:

    external FORTRAN class USEFUL_FUNCTIONS is
       -- external Fortran function that raises x to power y
       power(x:F_REAL,y:F_REAL):F_REAL;
    end;

F_DOUBLE

F_DOUBLE represents Fortran 77 double type.

    class F_REAL is
       create(x:FLTD):F_REAL is ...       -- construct from FLTD
       fltd:INT is ...                    -- FLTD version of self
       str:STR is ...                     -- string representation
       zero:SAME is ...                   -- zero and
       nil:SAME is ...                    -- nil values
       is_nil:BOOL is ...                 -- true if self is nil
       plus(i:SAME):SAME is ...
       minus(i:SAME):SAME is ...
       times(i:SAME):SAME is ...
       div(i:SAME):SAME is ...
       is_eq(i:SAME):BOOL is ...
       is_lt(i:SAME):BOOL is ...
    end;

Sather syntactic sugar for arithmetic and relational operations and operator precedence rules apply to F_DOUBLE.

F_LOGICAL

F_LOGICAL is a Sather class representing Fortran 77 logical type. It is "binary" compatible with Fortran's "logical" type (Sather BOOL has a vastly different representation in ICSI 1.1 compiler). In particular, F_LOGICAL occupies the same amount of space as Fortran integer and real types to conform to Fortran 77 rules.

    class F_LOGICAL is
       create(x:BOOL):F_LOGICAL is ...  -- construct from INT
       bool:BOOL is ...                 -- INT version of self
       str:STR is ...                   -- string representation
       not:SAME is ...
       is_eq(B:SAME):BOOL is ...
       f_or(b:SAME):SAME is ...
       f_and(b:SAME):SAME is ...
    end;

Logical operations are called f_or and f_and to avoid name collisions with short-circuited Sather operators `'and'` and `'or'`. The following function implementing exclusive or can be added to USEFUL_FUNCTIONS

    xor(a:F_LOGICAL,b:F_LOGICAL):F_LOGICAL is
       return (a.not.f_and(b)).f_or(a.f_and(b.not));
    end;

F_COMPLEX

F_COMPLEX is a Sather class binary compatible with Fortran 77 COMPLEX type. Although F_COMPLEX provides a constructor that accepts a variable of Sather CPX type, F_COMPLEX has a binary representation quite different from that of CPX. F_COMPLEX provides a set of features for setting and returning the values of the real and imaginary parts. It also provides useful constructors and supports a set of arithmetic operations.

    class F_COMPLEX is
       re:F_REAL is ...             -- return real part
       re(x:F_REAL) is ...          -- set real part
       im:F_REAL is ...             -- return imaginary part
       im(x:F_REAL) is ...          -- set imaginary part
       create(c:CPX):SAME is ...    -- create new and
                                    -- initialize to value of c
       create(re:F_REAL,im:F_REAL):SAME is ...
       create(re:FLT,im:FLT):SAME is ...
       create(fc:F_COMPLEX):SAME is ...
       cpx:CPX is ...               -- Sather comlplex type
       str:STR is ...               -- string representation
       zero:SAME is ...             -- zero and
       nil:SAME is ...              -- nil value
       is_nil:BOOL is ...           -- true if self is nil
       plus(c:SAME):SAME is ...
       minus(c:SAME):SAME is ...
       times(c:SAME):SAME is ...
       div(c:SAME):SAME is ...
       is_eq(c:SAME):BOOL is ...
    end;

This is a possible implementation of addition of F_COMPLEX numbers:

    plus(c:F_COMPLEX):F_COMPLEX is
       return #F_COMPLEX(re+c.re,im+c.im);
    end;

F_DOUBLE_COMPLEX

Similar to F_COMPLEX, F_DOUBLE_COMPLEX is a Sather class binary compatible with the Fortran double complex type. Double complex type is an extension to Fortran 77 supported by many F77 compiler. F_DOUBLE_COMPLEX class provides functionality similar to F_COMPLEX, but works with double precision floating point representations.

    class F_DOUBLE_COMPLEX is
       re:F_DOUBLE is ...           -- return real part
       re(x:F_DOUBLE) is ...        -- set real part
       im:F_DOUBLE is ...           -- return imagianry part
       im(x:F_DOUBLE) is ...        -- set imaginary part
       create(c:CPXD):SAME is ...   -- create new and
                                    -- initialize to value of c
       create(re:F_DOUBLE,im:F_DOUBLE):SAME is ...
       create(re:FLTD,im:FLTD):SAME is ...
       create(fc:F_DOUBLE_COMPLEX):SAME is ...
       cpxd:CPXD is ...             -- CPXD version of self
       str:STR is ...               -- string representation
       zero:SAME is ...             -- zero and
       nil:SAME is ...              -- nil value
       is_nil:BOOL is ...           -- true if self is nil
       plus(c:SAME):SAME is ...
       minus(c:SAME):SAME is ...
       times(c:SAME):SAME is ...
       div(c:SAME):SAME is ...
       is_eq(c:SAME):BOOL is ...
    end;

F_CHARACTER

F_CHARACTER is binary compatible with Fortran 77 types character and character*1. Fortran 77 character and character*1 types are, in fact, instances of character*n types with n set to 1. In Sather terms, they are strings with size always set to one. For parameter passing purposes, Fortran character and character*1 variables behave exactly as generic character*n types (the length of the string which is always one is passed as an extra parameter for each character or character*1 argument). Since the goal for F_CHARACTER is binary compatibility with Fortran, this is how F_CHARACTER class behave when a call crosses the language boundary. However, as long as F_CHARACTER variables stay within the Sather space, they are represented and passed to routines more efficiently, as a single byte. As a result, simple character operations performed on F_CHARACTER class in Sather are more efficient than their Fortran versions!

    class F_CHARACTER is
       create(c:CHAR):SAME is ...  -- create new and
                                   -- initialize to value of c
       char:CHAR is ...            -- CHAR version of self
       str:STR is ...              -- STR version of self
       zero:SAME is ...            -- zero
       is_eq(c:SAME):BOOL is ...
       is_lt(c:SAME):BOOL is ...
    end;

F_STRING

F_STRING is binary compatible with Fortran 77 character*n types. Note, that both F_CHARACTER and F_STRING can be used to interface with Fortran character*1 type, but F_CHARACTER yields better performance for computations performed in Sather.

F_STRING is internally represented by a tuple: the first field points to the string itself, and the second records the string length. An inter-language call requires that both be passed as separate arguments. In Parameter Passing, section 13.4 you find more information on this. Inside Sather however (calls using the Sather parameter passing convention), F_STRING is passed as a whole, which is slightly more efficient than the Fortran calls.

    class F_STRING is
       create(s:STR):SAME is ...   -- create new and
                                   -- initialize to value of s
       create(n:INT):SAME is ...   -- new of size n
       create(c:CHAR):SAME is ...  -- create from c
       address:C_CHAR_PTR is ...   -- the "string" part
       size:INT is ...             -- string length
       str:STR is ...              -- STR version of self
    end;

### 13.3.2 Fortran Array Classes

Providing a convenient array interface is an important goal for Sather/Fortran interoperability. A set of parametrized classes F_ARRAY{T<$F_SCALAR}, and F_ARRAYn{T<$F_SCALAR}, where n=2,3... are used for this purpose. Array classes can be parametrized by any of the scalar types. For example, F_ARRAY{F_INTEGER} corresponds to a Fortran 77 integer array type. Similarly, F_ARRAY2{F_REAL} represents a Fortran 77 two-dimensional array of real numbers.

F_ARRAY classes must be binary compatible with the Fortran 77 arrays and therefore they conform to the Fortran array layouts. For instance, this requires that in a two dimensional arrays successive elements of a column are in a contiguous memory locations (i.e. column major layout.) Note that regular Sather arrays (ARRAY{}, ARRAY2{}, etc.) support C-like row-major layout. Thus, creation of Fortran arrays based on Sather arrays may require a layout change. On the other hand, matrix classes provided by the Sather Math library have the same layout as Fortran arrays. F_ARRAY2 classes provide constructors from MAT classes that have reference semantics - thus the creation procedure is fairly inexpensive.

Combining materials from this chapter, and using Fortran array types, we can construct a simple Sather interface to standard Fortran BLAS single precision matrix multiplication routine as follows:

        SUBROUTINE SGEMM (TRANSA,TRANSB,M,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC)
        CHARACTER*1        TRANSA, TRANSB
        INTEGER            M, N, K, LDA, LDB, LDC
        REAL               ALPHA, BETA
        REAL               A( LDA, * ), B( LDB, * ), C( LDC, * )

    external FORTRAN class BLAS is
       sgemm(transa:F_CHARACTER, transb:F_CHARACTER, m,n,k:F_INTEGER,
             alpha:F_REAL, a:F_ARRAY2{F_REAL}, lda:F_INTEGER,
             b:F_ARRAY2{F_REAL}, ldb:F_INTEGER,beta:F_REAL,
             C:F_ARRAY2{F_REAL},ldc:F_INTEGER);
       -- this corresponds to the fortran BLAS signature
    end;

    class TEST_BLAS is
       main is
          fa,fb,fc:F_ARRAY2{F_REAL};
          sa,sb,sc:MAT;
          initialize(sa,sb,sc);
          fa := #(sa);  -- these creations has "reference" semantics
          fb := #(sb);
          fc := #(sc);
          dim:F_INTEGER := #(fa.size);
          TEST_BLAS::sgemm(#('N'),#('N'),#(sa.nr),#(sb.nc),#(sa.nc),#(1.0),
                           fa,#(sa.size1),fb,#(sb.size1),#(0.0),fc,#(sc.size1));
          -- at this point, both fc and sc have a multiplication result
       end;

       initialize(sa:MAT,sb:MAT,sc:MAT) is
           -- initialization code ...
       end;
    end;

We can go one step father and hide the details of Fortran implementation of sgemm entirely from the user:

    class MAT is
       ...
       various methods from MAT class
       ...
       times(m:SAME):SAME is
          -- multiply self by m and return the resulting matrix
          -- For efficiency, uses high-performance Fortran 77 BLAS sgemm
          res:MAT := #(nr,m.nc); -- storage for result
          fa,fb,fc:F_ARRAY2{F_REAL};
          fa := #(self);
          fb := #(m);
          fc := #(res);
          -- now, call the Fortran BLAS sgemm
          TEST_BLAS::sgemm(#('N'),#('N'),#(nr),#(m.nc),#(nc),#(1.0),
          fa,#(size1),fb,#(m.size1),#(0.0),fc,#(res.size1));
          -- at this point, both fc and res have a multiplication result
          return res;
       end;
    end;

    -- now it is really easy to multiply matrices!
    a,b,c:MAT;
    c := a*b;

This code shows that using high-performance Fortran BLAS in Sather is, in fact, much easier than in Fortran! The internal workings of BLAS could be buried in the libraries. As a result, matrix multiplication is expressed as easily as "a*b" in the example. If the code is compiled with compiler optimizations on, the Sather inlining stage eliminates an extra routine call, and the end result will be as efficient as calling "sgemm" from Fortran directly. However, we get away with not specifying about a dozen parameters in the most general case.

In the given example, the space for the multiplication result '`fc`' needs to be allocated in Sather (Fortran 77 has no means for a dynamic memory allocation). This is also necessary even when Fortran arrays are returned by functions.

Points to note

- Fortran arrays have a different layout from standard Sather arrays. In particular, in F_ARRAY2, consecutive elements in array columns occupy consecutive storage, while ARRAY2 has a row-major layout.

- MAT classes have the same layout as Fortran arrays, and conversion from MAT to F_ARRAY2 is very light-weight (reference semantics)

### 13.3.3 F_ROUT and F_HANDLER Types

Passing Routines as Arguments, F_ROUT{}

Fortran 77 supports passing procedures as arguments to subroutines and functions. It is desirable to be able to package a Sather routine and pass it as an argument to Fortran code. It may prove necessary for example, when Fortran numerical code expects a differentiation or integration function to be passed as an argument. Since we would like to exploit Sather flexibility and development speed whenever possible, a natural thing to do is to write such integration routines in Sather and pass them to numerical Fortran code.

Sather 1.1 provides a way to bundle any routine in the External class that supports the Fortran parameter passing convention and pass it as a functional argument to Fortran code that expects external procedures as parameters. A Fortran routine type F_ROUT{} serves this purpose. In many ways, F_ROUTs are similar to Sather routine closures. Just as routine closures, they are strongly typed and provide similar creation facilities. However, unlike routine closures, all arguments in the Fortran routine used for creation must be left unbound. This is necessary to adhere to Fortran semantics and for performance considerations.

'#F_ROUT(...)' is a creation expression that surrounds a Fortran calls with all arguments replaced by the underscore character '_'. For example, this code may be used to compute a distance between two points on the plane whose coordinates are represented by Fortran complex numbers:

    external FORTRAN class STAT is
       distance(point1:F_COMPLEX, point2:F_COMPLEX,res:F_REAL) is
          -- this routine is compiled using the Fortran parameter
          -- passing convention and name binding. It could be called
          -- from either Sather or Fortran
          x1:FLT := point1.re.flt; y1:FLT := point1.im.flt;
          x2:FLT := point2.re.flt; y2:FLT := point2.im.flt;
          res := #F_REAL(((x1-x2).square + (y1-y2).square).sqrt);
       end;

       -- this routine is implemented externally in Fortran
       process_points(array1:F_ARRAY{F_COMPLEX}, array2:F_ARRAY{F_COMPLEX},
                      func:F_ROUT{F_COMPLEX,F_COMPLEX,F_REAL),size:F_INTEGER);
    end;

In the above example, an externally implemented Fortran subroutine process_points expects two arrays of complex numbers and a function that will be applied to corresponding elements in the arrays:

        SUBROUTINE PROCESS_POINTS(ARRAY1,ARRAY2,FUNC,SIZE)
        COMPLEX ARRAY1(*), ARRAY2(*)
        EXTERNAL FUNC
        INTEGER SIZE

        REAL RES
        DO 10 I=1,SIZE
          CALL FUNC(ARRAY1(I),ARRAY2(I),RES)
          PRINT *, RES
    10  CONTINUE
        END

We can pass a routine defined in Sather to Fortran subroutine process_points the following way:

    -- This code appears in some STAT feature
    array1, array2:F_ARRAY{F_COMPLEX}
    -- some code to initialize array1 and array2

    rout:F_ROUT{F_COMPLEX,F_COMPLEX,F_REAL} := #F_ROUT(distance(_,_,_));
    process_points(array1,array2,rout); -- call Fortran code

Variables of F_ROUT type behave similarly to ROUT variables. It is possible to assign to such variables, pass them as parameters, etc.:

    rout:F_ROUT{F_COMPLEX,F_COMPLEX,F_REAL} := #F_ROUT(distance(_,_,_));

    rout1:F_ROUT{F_COMPLEX,F_COMPLEX,F_REAL);
    rout1 := rout;  -- F_ROUT assignment: lhs and rhs types are the same

Points to note

- F_ROUT type specifiers are very similar to routine closure type specifiers, but all types inside F_ROUT{} must be Fortran types.

- All call arguments in #F_ROUT() must remain unbound (Fortran 77 does not support binding subroutine or function arguments)

- F_ROUT variables could be assigned to just like other Sather variables. The types of the right-hand side and the left-hand sides of such assignments are strongly checked the same way as for other assignments.

- Unlike Sather routine closures, there may be no subtyping relationship between different F_ROUT types (this is because all Fortran types are concrete). Assignments or calls are possible only when the types are exactly the same.

- #F_ROUT() could be used just as well to bind externally defined Fortran routines (routines without bodies). These could be passed back to Fortran or used in Sather without restrictions.

- Type inferencing for F_ROUT creations works exactly as that for closure creation expressions.

- F_ROUT arguments cannot be passed as `"out"` or `"inout"` arguments.

Exceptional Condition Handling, F_HANDLER

It is possible in Fortran to anticipate exceptional conditions and have different flow paths depending on whether the called subroutine has terminated properly, or has detected abnormal circumstances. This is achieved using the alternate RETURN facility.

    *       A call to a subroutine with "alternate returns"
    *       This is a Fortran's way to handle exceptional conditions
    *       If, for some reason, FOO detects an abnormality
    *       it can choose to return to exception handlers
    *       (passed as labels 100 and 200), rather than to the caller
            CALL FOO(I,J,*100,*200)
    1       ....

    *       Handle exceptions
    *       Exception Handler 1
    100     ....
            GO TO 1
    200     Exception Handler 2
            ....
            GO TO 1

    *       A subroutine with alternate returns
    *       Two exception handlers are passed in (marked by *)
    *       RETURN 1 transfers control to the first handler, and
    *       RETURN 2 transfers control to the second handler
    *       "Normal" RETURN transfers control to the caller
            SUBROUTINE FOO(I,J,*,*)
            ...
    *       Detect abnormal conditions and transfer control to
            the appropriate exception handlers
            IF (I.EQ.0) RETURN 1
            IF (J.EQ.0) RETURN 2
            END

In the given example, the argument list of the call to subroutine FOO includes 2 labels corresponding to the exception handler entries. If an exceptional condition of some sort arises, FOO will transfer control to the appropriate exception handler (passed as an argument) rather than the caller. For example, if the value of argument I is 0, the control is transferred to exception handler 1, if J is 0, exception handler 2 handles the exception. The exception handlers are indicated by the dummy asterisk arguments in the subroutine argument list. Only subroutines are allowed to have such arguments.

Since alternate returns are a part of Fortran, they may be present in the interfaces provided by the Fortran libraries. It is, therefore, desirable to call such subroutines from Sather and provide exception handlers written in Sather for such calls.

The F_HANDLER class captures the essence of the Fortran exception handlers and could be passed in as an argument to a subroutine with alternate returns. F_HANDLER provides a single constructor create(rout:ROUT):SAME. The argument is a bound routine with no arguments since Fortran handlers do not have any arguments. Now, we will call the Fortran subroutine FOO, but supply Sather exception handlers at the moment of the call.

    class HANDLERS is
       h(i:INT) is
          #OUT + "Sather handler for Fortran exception " + i.str + "\n";
       end;

       create:SAME is
          return new;
       end;
    end;

    external FORTRAN class FOO is
       foo(i:F_INTEGER,j:F_INTEGER,handler1:F_HANDLER, handler2:F_HANDLER);
       -- note that foo can't have a return value - this is a Fortran
       -- restriction on subroutine with alternate returns
    end;

    -- code that calls Fortran FOO
    handlers:HANDLERS := #;
    handler1:F_HANDLER := #(bind(handlers.h(1))); -- create first handler
    handler2:F_HANDLER := #(bind(handlers.h(2))); -- create second handler
    FOO::foo(#(1),#(0),handler1,handler2);

When this code is executed, it prints: "Sather handler for Fortran exception 2".

F_HANDLER mechanism allows to integrate Fortran and Sather exceptions even more closely. For example, we can use Sather exception handlers that catch Fortran exceptions to raise standard Sather exceptions that are caught by the Sather protect mechanism. Essentially, this turns Fortran exception into regular Sather exceptions:

    class HANDLERS is
       r_h(i:INT) is
          raise "FORTRAN->Sather exception redirected by handler #" + i.str;
       end;
       create:SAME is return new; end;
    end;

    external FORTRAN class FOO is
       foo(i:F_INTEGER,j:F_INTEGER,handler1:F_HANDLER, handler2:F_HANDLER);
       -- note that foo can't have a return value - this is a Fortran
       -- restriction on subroutine with alternate returns
    end;

    -- code that calls Fortran FOO
    handlers:HANDLERS := #;
    redirect_handler1:F_HANDLER := #(bind(handlers.r_h(1)));
    redirect_handler2:F_HANDLER := #(bind(handlers.r_h(2)));
    protect
       FOO::foo(#(1),#(0),redirect_handler1,redirect_handler2);
    when STR then
       #OUT + "Sather exception for "+exception+\n";
    end

This code produces: "Sather exception for FORTRAN->Sather exception redirected by handler 2"

Points to note

- Only routines that have no return value can have F_HANDLER arguments. This is a Fortran restriction: only subroutines (not functions) can have alternate returns.

- F_HANDLER can be created from a standard closure with no arguments or return value: ROUT. An attempt to use closures of other types (like ROUT{INT}) is reported as an error. This restriction is also necessitated by the semantics of alternate returns in Fortran. Fortran exception handlers do not permit arguments.

- F_HANDLER types cannot be passed as "`out"` or "`inout"` arguments.

## 13.4 Parameter Passing

Some routines and calls in external Fortran classes are compiled using the Fortran parameter passing convention. This section describes how this is achieved. Routines without bodies in external Fortran classes and Fortran routines (routines whose return types and all arguments are Fortran types) are compiled as described below. The explanation is done in terms of mapping the original Sather signatures to C prototypes. All Fortran types are assumed to have corresponding C types defined. For example, F_INTEGER class maps onto F_INTEGER C type. See Portability Issues, section 13.5 for details on how this could be achieved in a portable fashion. The examples are used to illustrate parameter passing only - the actual binding of function names is irrelevant for this purpose.

### 13.4.1 Return Types

Routines that return F_INTEGER, F_REAL, F_LOGICAL, and F_DOUBLE map to C functions that return corresponding C types. A routine that returns F_COMPLEX or F_DOUBLE_COMPLEX is equivalent to a C routine with an extra initial arguments preceding other arguments in the argument list. This initial argument points to the storage for the return value.

    F_COMPLEX foo(i:F_INTEGER,a:F_REAL);
    -- this Sather signature is equivalent to
    void foo(F_COMPLEX* ret_val, F_INTEGER* i_address, F_REAL* a_address)

A routine that returns F_CHARACTER is mapped to a C routine with two additional arguments: a pointer to the data, and a string size, always set to 1 in the case of F_CHARACTER.

    F_CHARACTER foo(i:F_INTEGER, a:F_REAL);
    -- this Sather signature maps to
    void foo(F_CHARACTER* address, F_LENGTH size, F_INTEGER* i_address, F_REAL* a_address);

Similarly, a routine returning F_STRING is equivalent to a C routine with two additional initial arguments, a data pointer and a string length[16]

    F_STRING foo(i:F_INTEGER, a:F_REAL);
    -- this Sather signature maps to
    void foo(F_CHARACTER* address, F_LENGTH size, F_INTEGER* i, F_REAL* a);

### 13.4.2 Argument Types

All Fortran arguments are passed by reference. In addition, for each argument of type F_CHARACTER or F_STRING, an extra parameter whose value is the length of the string is appended to the end of the argument list.

    foo(i:F_INTEGER,c:F_CHARACTER,a:F_REAL):F_INTEGER
    -- this is mapped to
    F_INTEGER foo(F_INTEGER* i_address,F_CHARACTER*c_address,F_REAL* a_address,F_LENGTH c_length);
    -- all calls have c_length set to 1

    foo(i:F_INTEGER,s:F_STRING,a:F_REAL):F_INTEGER
    -- this is mapped to
    F_INTEGER foo(F_INTEGER* i_address,F_CHARACTER* s_address,F_REAL* a_address,F_LENGTH s_length);
    -- propoer s_length is supplied by the caller

Additional string length arguments are passed by value. If there are more than one F_CHARACTER or F_STRING arguments, the lengths are appended to the end of the list in the textual order of string arguments:

    foo(s1:F_STRING,i:F_INTEGER,s2:F_STRING,a:F_REAL);
    -- this is mapped to
    void foo(F_CHARACTER* s1_address,F_INTEGER* i_address,F_CHARACTER* s2_address,
             F_REAL a_address,F_LENGTH s1_length, F_LENGTH s2_length);

Sather signatures that have F_HANDLER arguments correspond to C integer functions whose return value represents the alternate return to take. The actual handlers are not passed to the Fortran code. Instead, code to do the branching based on the return value is emitted by the Sather compiler to conform to the alternate return semantics.

Arguments of type F_ROUT are passed as function pointers.

Thus, the entire C argument list including additional arguments consists of:

- one additional argument due to F_COMPLEX or F_DOUBLE_COMPLEX return type, or two additional arguments due to F_CHARACTER or F_STRING return type

- references to "normal" arguments corresponding to a Sather signature argument list

- additional arguments for each F_CHARACTER or F_STRING argument in the Sather signature

The following example combines all rules

    foo(s1:F_STRING, i:F_INTEGER, a:F_REAL, c:F_CHARACTER):F_COMPLEX
    -- is mapped to
    void foo(F_COMPLEX* ret_address, F_CHARACTER* s1_address,
             F_INTEGER* i_address, F_REAL* a_address,
             F_CHARACTER* c_address, F_LENGTH s1_length,
             F_LENGTH c_length);
    -- all Sather calls have c_length set to 1

### 13.4.3 OUT and INOUT Arguments

Sather 1.1 provides the extra flexibility of `'out'` and `'inout'` argument modes for Fortran calls. The Sather compiler ensures that the semantics of `'out'` and '`inout'` is preserved even when calls cross the Sather language boundaries. In particular, the changes to such arguments are not observed until the call is complete - thus the interlanguage calls have the same semantics as regular Sather calls.

This additional mechanism makes the semantics of some arguments visually explicit and consequently helps catch some bugs caused by the modification of '`in'` arguments (all Fortran arguments are passed by reference, and Fortran code can potentially modify all arguments without restrictions.) A special compiler option may enable checking the invariance of Fortran `'in'` arguments[17].

In the case of calling Fortran code, the Sather compiler ensures that the value/result semantics is preserved by the caller - the Sather compiler has no control over external Fortran code. This may involve copying `'inout'` arguments to temporaries and passing references to these temporaries to Fortran. In the case of Sather routines that are called from Fortran, the Sather compiler emits a special prologue for such routines to ensure the value/result semantics for the Fortran caller. In summary, the value/result semantics for external calls to Fortran is ensured by the caller, and for Sather routines that are meant to be called by Fortran it is implemented by the callee.

This example suggests how a signature for a routine that swaps two integers:

    SUBROUTINE SWAP(A,B)
    INTEGER A,B

    -- a Sather signature may look like
    swap(inout a:F_INTEGER, inout b:F_INTEGER);

Note that using argument modes in this example makes the semantics of the routine more obvious.

In the following example, compiling the program with all checks on may reveal a bug due to the incorrect modification of the vector sizes:

    SUBROUTINE ADD_VECTORS(A,B,RES,size)
    REAL A(*),B(*),RES(*)
    INTEGER SIZE

    -- Sather signature
    add_vectors(a,b,res:F_ARRAY{F_REAL}, size:F_INTEGER)
    -- size is an 'in' parameter and cannot be modified by Fortran code

In addition to extra debugging capabilities, `'in'` arguments are passed slightly more efficiently than '`out'` and `'inout'` arguments.

Points to note

- F_ROUT and F_HANDLER types cannot be `"out"` or `"inout"` arguments.

## 13.5 Portability Issues

This section discusses the portability of the Sather/Fortran interface. See Name Binding, section 13.2 about Various name binding portability issues. Issues relevant to code portability are addressed here.

### 13.5.1 Portability of the Interface Implementation Code

It is important to distinguish between portability of the Sather compiler module that implements the Sather/Fortran interface and the portability of the code it generates. The Fortran 77 interface module is written entirely in Sather and is integrated with the ICSI Sather compiler. The Fortran interface should be available on all platforms where the ICSI Sather compiler is available. In particular, it is available on most UNIX platforms.

### 13.5.2 Portability of the Generated Code

The Fortran 77 standards says that all Fortran 77 types except for COMPLEX, DOUBLE PRECISION, and CHARACTER of any flavor occupy a single "unit" of storage space. COMPLEX and DOUBLE PRECISION types take two "units" of storage. This may need to be adjusted accordingly when porting the Sather compiler to a different platform. A modification to "System/Common/fortran.h" may be necessary. "System/Common/fortran.h" contains a set of definitions for Fortran storage types used by the Sather/Fortran interface:

    typedef long int    F_INTEGER;
    typedef long int    F_LOGICAL;
    typedef float       F_REAL;
    typedef double      F_DOUBLE;
    typedef char        F_CHARACTER;
    typedef long int    F_LENGTH;
    typedef struct {
       F_REAL re, im;
    } F_COMPLEX_struct;
    typedef F_COMPLEX_struct F_COMPLEX;
    ...

This proves to be adequate for most UNIX platforms. On the Cray, however, both float and double types occupy the same storage, and to conform to Fortran 77 specification, fortran.h needs to be edited to define F_DOUBLE as "long double". For the Macintosh, however, it should be defined as "short double."

This is a full set of C types that are used by the interface as return and argument types:

    F_INTEGER
    F_LOGICAL
    F_REAL
    F_DOUBLE
    F_CHARACTER
    F_STRING
    F_LENGTH
    F_COMPLEX
    F_HANDLER
    F_ROUT
    integer or integer*4
    logical
    real
    double
    character or character*1
    character*n
    string length (same as F_INTEGER)
    complex
    call argument for a subroutine with alternate returns
    a routine passed as argument

Array types are represented as pointer to corresponding scalar types.

[back] [Abstract] [Copyright Notice] [Contents] [next]

Sather - A Language Manual