[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:

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


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

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:

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:

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


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

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


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:

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


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
4 November 1999
B. Gomes, D. Stoutamire, B. Vaysman and H. Klawitter
Norbert Nemec nobbi@gnu.org