MAINSAIL Compiler User's Guide, Chapter 7

previous   next   top   contents   index   framed top   this page unframed


7. The Foreign Language Interface

7.1. Introduction

This chapter describes how MAINSAIL can call procedures written in other languages and vice versa. The MAINSAIL component that provides this capability is called the Foreign Language Interface (FLI).

An FLI code generator generates assembly language code that acts as an interface between MAINSAIL and a foreign language. There are two kinds of interfaces and therefore two kinds of FLI code generators. An FLI code generator that generates an interface that allows a MAINSAIL MODULE to call a foreign procedure is called a Foreign Call Compiler (FCC); an FLI code generator that generates an interface that allows a foreign procedure to call a MAINSAIL MODULE is called a MAINSAIL Entry Compiler (MEC).

On different operating systems, different sets of FLI compilers may be available. On some operating systems, no FLI may be supported; on others, both FCC and MEC may be supported for several different languages. Refer to the appropriate operating-system-specific MAINSAIL user's guide for information on the FLI code generators available for your system.

The FLI-generated assembly language interface between MAINSAIL and a foreign procedure must be assembled with the host assembler and then linked with the foreign code and with a MAINSAIL bootstrap. For each FCC-generated interface with which it is linked, the bootstrap must contain the name of the corresponding FLI module in an internal table. These names are placed in the bootstrap by means of the utility MODULE CONF's FOREIGNMODULES command. The names are used to construct assembly language labels that permit linkage to be established to the foreign code.

No FOREIGNMODULES entry is made for an MEC-generated interface.

If execution begins in foreign code, the bootstrap must have the $foreignCodeStartsExecution configuration bit set. Use the CONF utility CONFIGURATIONBITS command to set this bit. It makes no sense to set this bit unless at least one MEC-generated interface is linked with the bootstrap.

The $foreignCodeStartsExecution bit may not be supported by all MEC's; consult the appropriate system-specific documentation for information.

The CONF entry in the Chapter 6 of the MAINSAIL Utilities User's Guide describes the use of CONF to make a MAINSAIL bootstrap.

Figure 7–1 shows a MAINSAIL bootstrap that has been linked to allow MAINSAIL to interface to and from foreign code. The arrows on the left show how MAINSAIL calls a foreign procedure and those on the right show how a foreign procedure calls MAINSAIL. In a real bootstrap, there may be any number of FCC-generated and MEC-generated interfaces.

Figure 7–1. Interfacing MAINSAIL to Other Languages
                 +--------------------------+
        +------> |                          | -------+
        |        | Standard MAINSAIL boot   |        |
        |  +---- |                          | <---+  |
        |  |     +--------------------------+     |  |
        |  |     |                          |     |  |
        |  +---> | Interface generated by   |     |  |
        |        |   an FCC                 |     |  |
        |  +---- |                          |     |  |
        |  |     +--------------------------+     |  |
        |  |     |                          | ----+  |
        |  |     | Interface generated by   |        |
        |  |     |   an MEC                 | <---+  |
        |  |     |                          |     |  |
        |  |     +--------------------------+     |  |
        |  |     |                          | ----+  |
        |  +---> | Foreign code             |        |
        |        |                          |        |
        |        +--------------------------+        |
        |                                            |
        |        +--------------------------+        |
        |        |                          |        |
        +------- | MAINSAIL module          | <------+
                 |                          |
                 +--------------------------+

7.2. Foreign Call Compiler

The bodies of foreign procedures are written in languages other than MAINSAIL. Theoretically, the foreign language could follow any calling convention, but in practice the only calling convention supported on most operating systems is that for the C language. Thus, foreign procedures are usually written in C or in assembly language obeying C calling conventions. (XIDAK could provide support for a non-C calling convention if necessary; contact XIDAK if you require such support.)

Each foreign procedure to be called from MAINSAIL must be declared to MAINSAIL with a MAINSAIL PROCEDURE header; i.e., the procedure result and parameter data types and passing mechanisms must be mapped into those provided by MAINSAIL. Each such MAINSAIL PROCEDURE header is then declared as part of the interface of a “dummy” MAINSAIL MODULE called a foreign MODULE. Within the implementation of the foreign MODULE, bodies for the fake interface PROCEDUREs must also be provided (to make the MODULE syntactically correct), but the bodies are ignored. The bodies of these dummy PROCEDUREs can be empty statements.

Each foreign MODULE is compiled with the FCC to generate an assembly interface to each procedure in the foreign MODULE.

The MAINSAIL declarations of foreign MODULEs must include only interface PROCEDUREs, not interface variables.

When the MAINSAIL intermodule call mechanism detects that the target MODULE is a foreign MODULE, it transfers control to the interface code generated for the foreign MODULE. This code sets up the parameters and environment for the foreign procedure and then invokes it. When the foreign procedure returns, the interface code sets up the parameters for MAINSAIL and the intermodule return mechanism handles the transition back to the MAINSAIL MODULE.

Parameter data type mappings vary from language to language and operating system to operating system. Appendix L of the MAINSAIL Language Manual describes the standard mapping for MAINSAIL Version 16.29 and later; for older versions of MAINSAIL, consult the system-specific MAINSAIL documentation for details. PRODUCES parameters passed from MAINSAIL to a foreign language procedure or vice versa are not necessarily cleared, and should not be assumed to be Zero upon entry to the target procedure.

Foreign procedures can be grouped arbitrarily into one or more MAINSAIL foreign MODULEs. Since, at the point of call, MAINSAIL does not know that it is calling a foreign MODULE, substitution of a MAINSAIL MODULE for a foreign MODULE is transparent. For example, if a C package is rewritten into a MAINSAIL MODULE, the MAINSAIL MODULE can be substituted for the foreign MODULE without the need to recompile other MAINSAIL MODULEs that call it.

A garbage collection can occur when a foreign MODULE is bound (by an explicit call to bind, or implicitly on the first call to the MODULE). The binding of the FLI MODULE causes the MAINSAIL runtime system to allocate some supporting data structures. Subsequent calls to an FLI MODULE cannot cause a garbage collection (unless the foreign language code calls back into MAINSAIL).

7.3. Foreign Call Compiler Example

Suppose that the programmer wishes to call some functions in a hypothetical C graphics package, with the following headers:

void line (ix1,iy1,ix2,iy2)
short ix1,iy1,ix2,iy2;

float circle (ix1,iy1,ix2,iy2,ix3,iy3)
short ix1,iy1,ix2,iy2,ix3,iy3;

void points (ix,iy,n)
short ix[],iy[];
short n;

One (or more) foreign MODULEs that describe the foreign procedures must be provided. First, determine the the MAINSAIL PROCEDURE declaration for each C function. Then declare each MAINSAIL PROCEDURE to be an interface PROCEDURE of a foreign MODULE. The following is a MODULE declaration for a MAINSAIL foreign MODULE GRPHCS that contains three foreign PROCEDUREs corresponding to the C functions, assuming typical data type correspondences between MAINSAIL and C (check Appendix L of the MAINSAIL Language Manual and your system-specific MAINSAIL documentation for details):

MODULE grphcs (
    
PROCEDURE line (
        
$CSHORT ix1,iy1,ix2,iy2);
    
$CFLOAT PROCEDURE circle (
        
$CSHORT ix1,iy1,ix2,iy2,ix3,iy3);
    
PROCEDURE points (
        
$CSHORT ARRAY(1 TO *) ix2,iy2$CSHORT n)
    );

Next write the MODULE GRPHCS. GRPHCS is compiled with the FCC and must be a syntactically correct MAINSAIL MODULE complete with a MODULE declaration and all interface PROCEDUREs. The PROCEDURE bodies may be empty, since the FCC ignores PROCEDURE bodies. In addition, all MAINSAIL MODULEs that call any of the foreign PROCEDUREs in the MODULE GRPHCS must contain the MODULE declaration for GRPHCS.

It is a good idea to put the MODULE declaration for GRPHCS in a file that is sourcefiled by or an intmod that is restored from by the MODULE GRPHCS and by each MAINSAIL MODULE that calls one or more of its interface PROCEDUREs. The following shows how to write the MODULE GRPHCS, assuming that the MODULE declaration for GRPHCS above resides in the intmod GHDR:

BEGIN "grphcs"

RESTOREFROM "gHdr"; # include grphcs MODULE declaration

PROCEDURE line (
    
$CSHORT ix1,iy1,ix2,iy2);;
$CFLOAT PROCEDURE circle (
    
$CSHORT ix1,iy1,ix2,iy2,ix3,iy3);;
PROCEDURE points (
    
$CSHORT ARRAY(1 TO *) ix2,iy2$CSHORT n);;

END "grphcs"

The RESTOREFROM directive pulls in the MODULE declaration for GRPHCS and is followed by interface PROCEDUREs with empty bodies (the second “;” after each PROCEDURE header terminates the null PROCEDURE body).

Compile GRPHCS with the FCC to generate the interface between MAINSAIL and each C function, as follows:


MAINSAIL (R) Compiler
Copyright (c) 1984-1998 by XIDAK, Inc., Point Arena,
 California, USA.


compile (? for help): grphcs.msl,<eol>
fli tc<eol>
<eol>
Opening intmod for $SYS...

grphcs.msl 1
Opening intmod for $SYS...
Output for GRPHCS stored on grphcs.assemblySuffix
Intmod for GRPHCS not stored

compile (? for help):

The interface generated and stored in grphcs.assemblySuffix is assembly code that translates the parameters from the MAINSAIL calling convention to the C calling convention, invokes the C function, and upon return from the C function, converts the parameters from the C calling convention back into the MAINSAIL calling convention and then returns to the MAINSAIL MODULE.

Use the CONF command FOREIGNMODULES to build a bootstrap that indicates to MAINSAIL that the MODULE GRPHCS is a foreign module. Assemble this new bootstrap and the output of the FCC and compile the C functions. Then link all of the above object files together (plus any platform-specific files) to create a new executable MAINSAIL bootstrap. Consult the appropriate operating-system-specific MAINSAIL user's guide to find out how to run CONF and how to assemble and link foreign code with MAINSAIL on your system.

7.4. MAINSAIL Entry Compiler

A normal MAINSAIL MODULE can be compiled with an MEC to generate an assembly interface that allows any foreign procedure to call any of the interface PROCEDUREs in the MAINSAIL MODULE. The foreign code is linked with the output of the MEC and with a MAINSAIL bootstrap to produce an executable file.

The foreign procedure can invoke the MAINSAIL PROCEDURE using its normal external call mechanism; i.e., the foreign procedure does not know that it is calling a MAINSAIL MODULE. Control is passed to the assembly interface code for the MAINSAIL PROCEDURE. This code sets up the MAINSAIL environment and parameters and invokes MAINSAIL to execute the PROCEDURE. When the MAINSAIL PROCEDURE completes execution, it returns control to the interface code that sets up the parameters for and returns control to the foreign procedure.

Since the return address to the foreign code is always available when foreign code calls into MAINSAIL, it is not necessary to include a table of foreign module names (addresses) in the bootstrap, as is the case when using the FCC. There is, however, a configuration bit that must be set if execution begins in foreign code rather than in MAINSAIL. When execution begins in foreign code, MAINSAIL must initialize itself the first time the foreign code calls a MAINSAIL PROCEDURE. The “foreign code starts execution” bit tells MAINSAIL not to print its identification banner or prompt for the next MODULE to execute when it initializes itself. Use the CONF CONFIGURATIONBITS command to set this bit and make a new MAINSAIL bootstrap if execution begins in foreign code.

7.5. MAINSAIL Entry Compiler Example

The following is a C function that calls an external procedure mean:

. . .
{
    
double a,b,c,mean;
    . . .
    
a = mean(b,c);
    . . .
}

Suppose that the procedure mean is written in MAINSAIL:

BEGIN "foo"

MODULE foo (LONG REAL PROCEDURE mean (LONG REAL a,b));

LONG REAL PROCEDURE mean (LONG REAL a,b);
RETURN((a + b) / 2.0L);

END "foo"

The MEC is used to generate the interface that allows mean to be called from the C function.

Compile the MODULE FOO with the MEC to produce the assembly interface code for the procedure mean and with the normal MAINSAIL compiler to produce the executable MAINSAIL object MODULE for FOO:


MAINSAIL (R) Compiler
Copyright (c) 1984-1998 by XIDAK, Inc., Point Arena,
 California, USA.


compile (? for help): foo.msl<eol>
Opening intmod for $SYS...

foo.msl 1
Objmod for FOO on foo-xxx.obj
Intmod for FOO not stored

compile (? for help): foo.msl,<eol>
fli fc<eol>
<eol>
Opening intmod for $SYS...

grphcs.msl 1

Output for GRPHCS stored on grphcs.assemblySuffix
Intmod for GRPHCS not stored

compile (? for help):

If execution begins in the C function, use the CONF CONFIGURATIONBITS command to set the “foreign code starts execution” configuration bit and make a new MAINSAIL bootstrap. Link the C code with the MAINSAIL bootstrap and with the interface code generated by the MEC (and possibly with other platform-specific files). Refer to the appropriate operating-system-specific MAINSAIL user's guide for information on how to assemble and link foreign code with MAINSAIL on your system.

7.6. Foreign Labels and the ENCODE Directive

By default, the FCC assumes that the labels for foreign procedures are derived by some transformation of the corresponding MAINSAIL PROCEDURE name, and the MEC generates labels based on the same transformation of the corresponding MAINSAIL PROCEDURE name. The default transformation is operating-system-dependent and is described in each operating-system-specific MAINSAIL user's guide.

The default transformation may be overridden by means of the ENCODE directive, which may appear anywhere within the foreign MODULE. The form of the ENCODE directive is

ENCODE p1 s1p2 s2, ..., pn sn;

where the pi are interface PROCEDURE identifiers and the si are STRING constants. The STRINGs si may be arbitrary STRING constants and are used as the labels for their respective pi when pi are compiled with the FLI compiler. The user is responsible for ensuring that the si conform to the operating-system-dependent assembler's and linker's rules for valid labels.

If the following MODULE BAR of is compiled with the FCC compiler, the FCC compiler assumes that the foreign procedure names corresponding to abc and def are _AbC and D$xxx, respectively:

BEGIN "bar"

MODULE bar (
    
PROCEDURE abc (REAL r);
    
INTEGER PROCEDURE def;
);

ENCODE abc "_AbC", def "D$xxx";

PROCEDURE abc (REAL r);
body for abc;

INTEGER PROCEDURE def;
body for def;

END "bar"

If BAR is compiled with the MEC, the labels generated for the procedures corresponding to abc and def are _AbC and D$xxx, respectively. If _AbC and D$xxx are not valid labels on the system for which BAR is compiled, assembly or linkage errors may result.

7.7. Matching Parameters

MAINSAIL parameter data types and passing mechanisms (USES, PRODUCES, MODIFIES, and $REFERENCE) must be mapped onto the foreign language data types, and vice versa. For example, “value” parameters are passed as MAINSAIL USES parameters, and “reference” parameters are passed as MAINSAIL PRODUCES, MODIFIES, or $REFERENCE parameters. The exact mapping of data types is operating-system- and language-dependent, and is documented in the operating-system-specific MAINSAIL user's guide for your system.

The mapping of a MAINSAIL ARRAY or record into a foreign language requires a knowledge of the layout in memory of both the MAINSAIL data structure and the corresponding foreign data structure. For C, however, the mapping of data structures may be simplified by the use of aligned CLASSes; see Section 9.9 of the MAINSAIL Language Manual and the relevant operating-system-specific MAINSAIL documentation. Also, a C function is provided to get the address of the first element of a MAINSAIL array; see Section 7.9. XIDAK does not otherwise document memory usage conventions for foreign languages; consult the documentation provided by the manufacturer of the target language if necessary.

7.8. MAINSAIL Language and Runtime System Features to Support Foreign Language Data Access

A variety of features in the MAINSAIL language and runtime system support the ability to share data between MAINSAIL and foreign languages. Except where mentioned otherwise, these features are described in the MAINSAIL Language Manual; you should consult the Manual's index for detailed descriptions of these features.

The foreign data access features fall into the following categories:

The foreign data access features provide equivalents for most foreign language data structures, but do not directly provide for access to the following C features:

In general, direct foreign data access to C++ is not available; MAINSAIL and C++ must communicate using C as an intermediary.

For each target platform default values are chosen for certain foreign access features, such as the alignment rules for aligned CLASSes or the data type sizes associated with the C data type macros. The default values for each platform are based on what XIDAK believes is the most commonly used C language implementation on that platform. Though some of the defaults can be overridden on a per-use basis, providing the ability to share data with multiple foreign languages with different characteristics, the expectation is that the default values will almost always be the ones you need. If you need to use a C dialect or other foreign language for which the defaults are not appropriate, please contact XIDAK, and we will see if we can improve MAINSAIL's ability to communicate with that compiler or language.

7.8.1. Foreign Data Access System PROCEDUREs

The miscellaneous system PROCEDUREs and macros for manipulating foreign language data in MAINSAIL and vice versa include:

All of these are documented in the MAINSAIL Language Manual.

7.9. C Function for Accessing MAINSAIL Arrays: xiAdrOfFirstElement

The C routine xiAdrOfFirstElement can be used to get the first element of a MAINSAIL ARRAY that is a field or element of a data structure passed to C (MAINSAIL ARRAY parameters to an FLI PROCEDURE are normally passed as the address of the first element of the ARRAY).

xiAdrOfFirstElement is declared as:

void *xiAdrOfFirstElement (void *mslAry)

where mslAry is the address passed to C corresponding to an FLI ARRAY parameter; xiAdrOfFirstElement returns the address of mslAry's first element.

To call xiAdrOfFirstElement, make sure your C code includes:

#include <xiadrof1st.h>

When compiling C code that calls xiAdrOfFirstElement, make sure that the MAINSAIL directory is in the C compiler's search path for header files. For example, on most flavors of UNIX, use the switch -ImainsailDirectoryName to the C compiler to tell it to look in the MAINSAIL directory for header files.

7.10. Foreign Data Structure Example

Consider the following C declarations for tagged geometric figures:

struct point {
    
float x,y;
};

struct triangle {
    
short shapeCode;
    
char name[100];
    
struct point origin;
    
struct point points[3];
};

struct rectangle {
    
short shapeCode;
    
char name[100];
    
struct point origin;
    
struct point oppositeCornerPoints[2];
};

struct circle {
    
short shapeCode;
    
char name[100];
    
struct point origin;
    
float radius;
};

(You might prefer to use a C union to capture the fact that members up through origin are the same for triangle, rectangle, and circle; shapeCode might be an enum. However, unions and enums do not have direct MAINSAIL equivalents.)

The corresponding MAINSAIL declarations might look like (see Appendix L of the MAINSAIL Language Manual for a discussion of the macros $CFLOAT, $CSHORT, etc.):

$ALIGN CLASS point (
    
$CFLOAT x,y;
);

$ALIGN CLASS geometricFigure (
    
$CSHORT shapeCode;
    
$CHAR $INPLACEARRAY(0 TO 99) name;
    
$CSTRUCT(pointorigin;
);

CLASS(geometricFiguretriangle (
    
$CSTRUCT(point$INPLACEARRAY(0 TO 2) points;
);

CLASS(geometricFigurerectangle (
    
$CSTRUCT(point$INPLACEARRAY(0 TO 1) oppositeCornerPoints;
);

CLASS(geometricFigurecircle (
    
$CFLOAT radius;
);

When passing a MAINSAIL record of these CLASSes to C, you may declare the parameters in the FLI PROCEDURE as:

In any case, you should not pass POINTERs or $REFERENCEs to records in unanchored areas to C if C might call back into MAINSAIL before returning, or might hold on to the POINTER.

Data structures of these CLASSes allocated in C and passed to MAINSAIL should be declared in MAINSAIL as classified ADDRESSes.

For example, a MAINSAIL FLI MODULE might contain the following PROCEDURE headers:

PROCEDURE   foo         (POINTER(rectangler);

ADDRESS(circle)
PROCEDURE   bar         ($REFERENCE $RECORD(trianglet);

corresponding to C function headers:

void foo (r)
struct rectangle *r;

struct circle *bar (t)
struct triangle *t;

In MAINSAIL, if you had the declarations:

POINTER(rectanglemyRect;
$RECORD(trianglemyTri;
ADDRESS(circlemyCirc;

you might call foo and bar with:

foo(myRect);
myCirc := bar(myTri);

where myRect should have been allocated in an anchored area, and myCirc is allocated by C.

7.11. Foreign Code and Garbage Collection

On operating systems that support both the FCC and the MEC, it is possible for MAINSAIL to call foreign code that then calls back into MAINSAIL, and for foreign code to call a MAINSAIL PROCEDURE that then calls out to foreign code. MAINSAIL must lock out collections if any collectable parameters (i.e., POINTERs, STRINGs, or dynamic ARRAYs) are passed between MAINSAIL and the foreign code; if MAINSAIL did a garbage collection while there were pending returns to foreign code, collectable variables copied into the data areas for the foreign code would not be updated and might no longer be valid. Collections are not locked out if no collectables are passed between MAINSAIL and the foreign code.

If the foreign code may hang onto addresses passed in from MAINSAIL even after the call to foreign code has completed, and the MAINSAIL addresses point to MAINSAIL dynamic objects or STRING text in STRING space, then the MAINSAIL dynamic objects or STRINGs should be located in anchored areas, as described in Section 25.4 of the MAINSAIL Language Manual.

The information above is subject to change. In particular, it may become true in some future release that collections are locked out only when POINTERs or STRINGs pointing into non-anchored areas are passed to foreign code. Currently, collections are locked out even when a POINTER or STRING argument points into an anchored area, or when a STRING argument points into static space, even though a garbage collection could not cause such data to move and thereby invalidate addresses in foreign code.

7.12. Foreign Code and Exceptions

If MAINSAIL traps operating-system-specific exceptions and such an exception occurs in foreign code, then the normal MAINSAIL error handling mechanism is given control. The appropriate MAINSAIL exception is raised and, if it is not handled, an error message that indicates that the exception occurred in foreign code and the name and offset of the most recently executing MAINSAIL MODULE are printed.

7.12.1. C Exceptions and MAINSAIL Coroutines

There are restrictions on the use of exception-like mechanisms in foreign code as they relate to MAINSAIL code. The restrictions below assume the foreign language in question is C, but apply to any other language that supports similar concepts:

7.13. FLI MODULE and PROCEDURE Name Restrictions

Because FLI code generators produce assembly language output, and because some of the MAINSAIL identifiers within an FLI MODULE are used as labels in that assembly language output, an FLI MODULE name or entry FLI PROCEDURE label cannot be a reserved word in the assembler in question. On most platforms, this means that you cannot name a MODULE or PROCEDURE with an identifier that is an assembler directive or instruction mnemonic.

Since assembly language syntax varies from platform to platform, you must check the assembly language manual in question to determine which identifiers are unsuitable for MAINSAIL FLI MODULE and PROCEDURE names.

7.14. FLI Parameter Restrictions

Currently, inplace ARRAYs and records can be passed to or from a foreign language only as $REFERENCE parameters.
previous   next   top   contents   index   framed top   this page unframed

MAINSAIL Compiler User's Guide, Chapter 7