previous next top contents index framed top this page unframed
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.
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.
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.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.
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 s1, p2 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.
All of these are documented in the
MAINSAIL Language Manual.
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:
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.
(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.):
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:
corresponding to C function headers:
In MAINSAIL, if you had the
declarations:
you might call foo and bar with:
where myRect should have been
allocated in an anchored area, and
myCirc is allocated by C.
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;
};
$ALIGN CLASS point (
$CFLOAT x,y;
);
$ALIGN CLASS geometricFigure (
$CSHORT shapeCode;
$CHAR $INPLACEARRAY(0 TO 99) name;
$CSTRUCT(point) origin;
);
CLASS(geometricFigure) triangle (
$CSTRUCT(point) $INPLACEARRAY(0 TO 2) points;
);
CLASS(geometricFigure) rectangle (
$CSTRUCT(point) $INPLACEARRAY(0 TO 1) oppositeCornerPoints;
);
CLASS(geometricFigure) circle (
$CFLOAT radius;
);
PROCEDURE foo (POINTER(rectangle) r);
ADDRESS(circle)
PROCEDURE bar ($REFERENCE $RECORD(triangle) t);
void foo (r)
struct rectangle *r;
struct circle *bar (t)
struct triangle *t;
POINTER(rectangle) myRect;
$RECORD(triangle) myTri;
ADDRESS(circle) myCirc;
foo(myRect);
myCirc := bar(myTri);
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:
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.
MAINSAIL Compiler User's Guide, Chapter 7