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
Section 13.1 introduces the Sather/Fortran interface and provides a few illustrative examples. Section 13.2 talks about binding Sather entities to corresponding Fortran entities. Section 13.3 provides a mapping of "basic" Sather types to Fortran types. Section 13.4 explains how arguments in a Sather call are passed to a Fortran procedure or function that implements the feature. Finally, 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 section Datatype Mapping on page 156. 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 section Datatype Mapping on page 156.
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.
![](DescriptionX2Elast-cha-1-image-1.gif)
| -- 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:
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
F_INTEGER
Fortran 77 | Sather class | Features provided by the library |
INTEGER | F_INTEGER | create(x:INT):F_INTEGER -- construct from INT int:INT -- INT version of self str:STR -- string representation zero:SAME -- zero and nil:SAME -- nil values is_nil:BOOL -- true if self is nil plus(i:SAME):SAME minus(i:SAME):SAME times(i:SAME):SAME div(i:SAME):SAME is_eq(i:SAME):BOOL is_lt(i:SAME):BOOL |
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.
F_REAL
Fortran 77 | Sather class | Features provided by the library |
REAL | F_REAL | create(x:FLT):F_REAL -- construct from FLT flt:INT -- FLT version of self str:STR -- string representation zero:SAME -- zero and nil:SAME -- nil values is_nil:BOOL -- true if self is nil plus(i:SAME):SAME minus(i:SAME):SAME times(i:SAME):SAME div(i:SAME):SAME is_eq(i:SAME):BOOL is_lt(i:SAME):BOOL |
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.
F_DOUBLE
Fortran 77 | Sather class | Features provided by the library |
REAL | F_REAL | create(x:FLTD):F_REAL -- construct from FLTD fltd:INT -- FLTD version of self str:STR -- string representation zero:SAME -- zero and nil:SAME -- nil values is_nil:BOOL -- true if self is nil plus(i:SAME):SAME minus(i:SAME):SAME times(i:SAME):SAME div(i:SAME):SAME is_eq(i:SAME):BOOL is_lt(i:SAME):BOOL |
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.
F_LOGICAL
Fortran 77 | Sather class | Features provided by the library |
LOGICAL | F_LOGICAL | create(x:BOOL):F_LOGICAL -- construct from INT bool:BOOL -- INT version of self str:STR -- string representation not:SAME is_eq(B:SAME):BOOL f_or(b:SAME):SAME f_and(b:SAME):SAME |
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.
F_COMPLEX
Fortran 77 | Sather class | Features provided by the library |
COMPLEX | F_COMPLEX | re:F_REAL -- return real part re(x:F_REAL) -- set real part im:F_REAL -- return imaginary part im(x:F_REAL) -- set imaginary part create(c:CPX):SAME -- create new and -- initialize to value of c create(re:F_REAL,im:F_REAL):SAME create(re:FLT,im:FLT):SAME create(fc:F_COMPLEX):SAME cpx:CPX -- Sather comlplex type str:STR -- string representation zero:SAME -- zero and nil:SAME -- nil value is_nil:BOOL -- true if self is nil plus(c:SAME):SAME minus(c:SAME):SAME times(c:SAME):SAME div(c:SAME):SAME is_eq(c:SAME):BOOL |
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.
F_DOUBLE_COMPLEX
Fortran 77 | Sather class | Features provided by the library |
double complex | F_DOUBLE_ COMPLEX | re:F_DOUBLE -- return real part re(x:F_DOUBLE) -- set real part im:F_DOUBLE -- return imagianry part im(x:F_DOUBLE) -- set imaginary part create(c:CPXD):SAME -- create new and -- initialize to value of c create(re:F_DOUBLE,im:F_DOUBLE):SAME create(re:FLTD,im:FLTD):SAME create(fc:F_DOUBLE_COMPLEX):SAME cpxd:CPXD -- CPXD version of self str:STR -- string representation zero:SAME -- zero and nil:SAME -- nil value is_nil:BOOL -- true if self is nil plus(c:SAME):SAME minus(c:SAME):SAME times(c:SAME):SAME div(c:SAME):SAME is_eq(c:SAME):BOOL |
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!
F_CHARACTER
Fortran 77 | Sather class | Features provided by the library |
character character*1 | F_CHARACTER | create(c:CHAR):SAME -- create new and -- initialize to value of c char:CHAR -- CHAR version of self str:STR -- STR version of self zero:SAME -- zero is_eq(c:SAME):BOOL is_lt(c:SAME):BOOL |
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. The section Parameter Passing on page 171 provides 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.
F_STRING
Fortran 77 | Sather class | Features provided by the library |
character*n | F_STRING | create(s:STR):SAME -- create new and -- initialize to value of s create(n:INT):SAME -- new of size n create(c:CHAR):SAME -- create from c address:C_CHAR_PTR -- the "string" part size:INT -- string length str:STR -- STR version of self |
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. Section Portability Issues on page 174 describes 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.[1]
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[2].
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. Various name binding portability issues where covered in section Name Binding on page 153. 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_ROUTinteger 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.
Interfacing with ANSI C
This chapter describes interfacing with ANSI C , X3.159-1989. Section 14.1 gives a short overview of the C interface functionality. Section 14.2 introduces built-in C types provided by the extended Sather library. Section 14.3 talks about user defined external C types, constants, attributes, and shared elements. Section 14.4 covers parameter passing issues, and finally section 14.5 describes the inline C facility.
14.1 Overall Organization
An external class which interfaces to ANSI C is designated with the language identifier 'C'. Types defined by external C classes are called external C types. Similar to external Fortran types, signatures without bodies (abstract signatures) are allowed in external C types. Such signatures must contain only built-in or user defined C types and they are implemented externally in ANSI C. Abstract iterator signatures are not allowed in external C classes. Routines with bodies whose signatures contain only C types may be called from C. Routines with bodies whose signatures use types other than C types are regular Sather routines and are not accessible from C. External C routines cannot be overloaded.
In contrast with the external Fortran classes, external C classes may have attributes and objects of external C types may exist. All attributes must also be of C types. The C interface provides a naming facility that allows interoperability with the existing C header files.
Global C variables can be accessed as shared attributes of external C classes.
C symbols are generated by applying a platform specific C name binding convention to the textual external C routine names. It is also possible to explicitly specify name binding for external C classes.
Finally, it is possible to inline ANSI C code into Sather sources. This allows for even greater flexibility in achieving Sather/C interoperability.
14.2 Built-in C classes
The following C types are built into the extended library:
Sather Class | ANSI C type | Sather Class | ANSI C type |
C_CHAR | char | C_UNSIGNED_CHAR_PTR | unsigned char * |
C_UNSIGNED_CHAR | unsigned char | C_SIGNED_CHAR_PTR | signed char * |
C_SIGNED_CHAR | signed char | C_SHORT_PTR | short * |
C_SHORT | short | C_INT_PTR | int * |
C_INT | int | C_LONG_PTR | long * |
C_LONG | long | C_UNSIGNED_SHORT_PTR | unsigned short * |
C_UNSIGNED_SHORT | unsigned short | C_UNSIGNED_INT_PTR | unsigned int * |
C_UNSIGNED_INT | unsigned int | C_UNSIGNED_LONG_PTR | unsigned long * |
C_UNSIGNED_LONG | signed long | C_FLOAT_PTR | float * |
C_FLOAT | float | C_DOUBLE_PTR | double * |
C_DOUBLE | double | C_LONG_DOUBLE_PTR | long double * |
C_LONG_DOUBLE | long double | C_SIZE_T | size_t |
C_PTR | void * | C_PTRDIFF_T | ptrdiff_t |
C_CHAR_PTR | char * | | |
Variable of the built-in types are binary compatible with the corresponding C types. These classes define appropriate creation routines which may be used for convenient casting between Sather and C types. Also, many basic operations on the built-in C types are provided by the library. For example, it is not necessary to call external C code to add two C_INT variables. All operations on built-in C types defined by the library have the ANSI C semantics. Syntactic sugar for the built-in C types is defined exactly as for "regular" Sather classes.
-- "basic" operations may be done in Sather
a:C_LONG := #(10);
b:C_LONG := #(5);
c::= a + b;
#OUT + c.str + " should be 15\n"; |
'AREF{T}' defines a routine 'array_ptr:C_PTR' which may be used to obtain a pointer to the first item in the array portion of Sather objects. The external routine may modify the contents of this array portion, but must not store the pointer; there is no guarantee that the pointer will remain valid after the external routine returns. This restriction ensures that the Sather type system and garbage collector will not be corrupted by external code while not sacrificing efficiency for the most important cases.
The following example shows how a Sather array could be passed to external C functions:
/* ANSI C prototypes for functions called from Sather */
void clear(void* p, int size);
void better_clear(int *, int size);
external C class PROCESS_ARRAYS is
-- routines implemented externally in C that zero
-- all elements in an integer array of a specified size
clear(p:C_PTR, size:C_INT);
better_clear(p:C_INT_PTR, size:C_INT);
end;
-- This code demonstrates how to call external C routines
a:ARRAY{INT} := #(10);
-- this call just passes an array portion and avoids typecheking
-- This is not recommended ("a" could be of type ARRAY{CHAR} and the
-- call would still compile resulting in a runtime error)
PROCESS_ARRAYS::clear(a.arr_ptr, #(a.size);
-- this is a better sequence achieving the same result
-- if "a" is not an array of integers, an error is reported
PROCESS_ARRAYS::better_clear(#C_INT_PTR(a), #(a.size)); |
The second call is type-safe. It exploits the constructor for C_INT_PTR that allows creation from ARRAY{INT}.
14.3 User-defined External C types
User-defined external C classes are used for multiple purposes. C routines in external C classes implement Sather/C and C/Sather call interfaces. In addition, objects of external C types could be created and passed to or received from C. C global variables are accessed from Sather as shared attributes of external C classes.
14.3.1 Constants and C binding names
Constants are allowed in external C classes. The rules for constant initialization are the same as for constants in "regular" Sather classes.
There are two constant features of external C classes that have a special semantics. If present, the STR constant 'C_name' may be used to force a particular C declaration for an external C type. Similarly the STR constant 'C_header' may be used to specify a list of C header files that should be included in each file in which the C declaration appears.
The STR constant 'C_name' provides a C binding name for the type in which it occurs. The STR constant 'C_header' must be initialized to a space separated list of header files (the standard C notation <foo.h> is allowed). Note that if constants C_name and C_header are absent, the Sather compiler generates layouts for the external C objects. If they are present, no layouts are generated and the necessary types must be defined in the specified header files. In this case, it is the responsibility of the programmer to ensure that attribute names are exactly as the structure filed names provided by the header files.
Examples
external C class BAR is
attr bar_attr_int:C_INT;
attr bar_attr_float:C_FLOAT;
-- the constructor is defined in C
create_bar:BAR;
-- this routine that does some processing of bar is also
-- defined in C
process_bar(bar:BAR);
end;
-- create an object of type BAR by calling an external
-- C constructor
bar:BAR := BAR::create_bar;
-- now pass "bar" back to C from processing
BAR::process_bar(bar);
|
In this example, the Sather compiler generates the layout for the external objec BARt. The corresponding C layout and prototypes of C functions that are called from Sather are below:
| typedef struct {
int integer_field;
float float_field;
} *C_BAR;
/* Note that C names for the type and struct fields could be
different from the corresponding names in Sather */
C_BAR create_bar();
void process_bar(C_BAR bar); |
|
This is a similar example, but an existing C header file is used with Sather code:
external C class BAR is
const C_name:STR := "C_BAR"; -- C binding name for the type
const C_header:STR := "bar.h <stdlib.h>";
attr integer_field:C_INT;
attr float_field:C_FLOAT; -- the constructor is defined in C
create_bar:BAR;
-- this routine that does some processing of bar is also
-- defined in C
process_bar(bar:BAR);
end;
-- code that creates an object of type BAR by calling an external
-- C constructor and then passes the object back to C
bar:BAR := BAR::create_bar;
-- now pass "bar" back to C from processing
BAR::process_bar(bar);
| |
The C header "bar.h" contains the following:
typedef struct {
int integer_field;
float float_field;
} *C_BAR;
/* Note that C names for the type must be exactly as the binding C
name specified by the C_name attribute.
also, struct field names must be exactly the same as attribute
names in the external C class*/
C_BAR create_bar();
void process_bar(C_BAR bar); |
This creates a Sather type 'X_WIDGET' which may be used to declare variables, parameterize classes, and so forth. Furthermore, the C declaration used for variables of type 'X_WIDGET' will be 'struct XSomeWidget *'. Any generated C file containing any variable of this type will also include '<widgets.h>'
external C class X_WIDGET is
const C_name:STR:=
"struct XSomeWidget *";
const C_header:STR:=
"<widgets.h>";
end; -- external class X_WIDGET |
14.3.2 Attributes and C structs
Attributes and C structs
Attributes may be placed in external C classes; they are interpreted as fields of a C struct. If the layout of the class is generated by Sather (C_name and C_header symbolic constants are absent), then attributes can have any names. If a C layout from a header file specified by C_header is used, attribute textual names must be exactly the same as a struct filed names from a corresponding C type. It is the responsibility of the programmer to ensure this correspondence.
Points to note
- External C class attributes may only have built-in or user-defined external C types.
- Class constants do not contribute anything to the class layouts; all attributes do.
14.3.3 Shared Attributes and C globals
Global C variables may be accessed from Sather as shared attributes of external C classes. Such shared attributes must have names corresponding to those of C globals. Similar to constants, shared attributes do not contribute to the storage needed to layout the class objects.
external C class FOO is
C_name:STR := "FOO";
C_header:STR := "foo.h";
shared foo:FOO:
attr val:C_INT;
-- this is implemented in C
create_foo:FOO;
end;
-- accessing a global C variable
FOO::foo := FOO::create_foo;
FOO::foo.val := #(10); |
#ifndef _FOO_H_
#define _FOO_H_
typedef struct {
int val;
} *FOO;
FOO create_foo();
#endif _FOO_H_
|
/* in some C file */
FOO foo; |
14.4 Parameter Passing
The ANSI C standard prescribes that a copy is made of each call argument and all argument-passing is done strictly by value. To conform to ANSI C, all "in" arguments are passed by value. In the case of the built-in C types, a copy of a variable is passed. In the case of user defined external C types, a pointer to the object is copied and passed by value.
In addition, for extra flexibility, Sather supports "out" and "inout" argument modes for external C routines. "out" and "inout" arguments are passed by a pointer to a local, which may be legally modified by the called routine. The Sather implementation guarantee that such modifications cannot be observed until the routine returns. For C routines called from Sather this is guaranteed by emitting special code for the caller. For Sather routines that may be called from C, this is guaranteed by emitting special function prologues for the callee.
14.5 Inlining C Code
Sometimes it isn't possible to decide at the time the external C class is written whether a routine will be implemented in the C code with a macro. This presents a portability problem, because the writer of the external class can't know ahead of time whether the routine will be obtained by linking or by a header file. Such petulant cases can be dealt with by the call 'SYS::inlined_C'. The argument must be a string literal, and is placed directly into the generated code, except that identifiers following '#' that correspond to locals and arguments are translated into the appropriate C names.
[1] The current Sather 1.1 implementation disallows returning Fortran strings of size greater than 32 bytes. This restriction may be lifted in the future releases.
[2] The ICSI Sather 1.1 compiler currently does not implement this functionality.