Previous Up Next

6.10  Compiling and loading object code

Traditionally when an ECLiPSe file is compiled, it is loaded immediately into the system. Sometime it is useful to generate `object code' which can then be loaded later, perhaps even in a different session of ECLiPSe, maybe on a different platform. This functionality is provided by the fcompile library.

In order to use this facility, the fcompile library should be loaded first:
:- lib(fcompile).
fcompile(+File) can then be used to generate an object file from an ECLiPSe source file. The object file has the same base name as the source file File, but with the suffix .eco attached.

fcompile generates an object file by compiling an ECLiPSe source file normally, and then disassembling the compiled code into an object form, which is written to the object file. This object form is platform independent and can be loaded into ECLiPSe running on a different platform from the one that generated it (see section 6.10.1 for restrictions).

The object file is generated in the current working directory.

Options can be specified for fcompile by using fcompile/2.

fcompile is designed mainly for generating an object file for a whole module. The include directive allows multiple source files to be compiled into one object file. When fcompile encounters an include directive in the source file:
:- include(File).
it will generate the object code for the file(s) in File in place of the directive. The effect is as if the actual source code for file(s) was written at the point of the include directive. Note that this can have a different semantics from recursively compiling files using the compile directive, because any new module in a recursively compiled file ends with the end of that file. With include, any new modules defined in that file will not end with the file. Thus, a compile directive should not be changed to an include directive if the target file contains definitions for a separate module.

The object code file (with .eco suffix) will be loaded in preference to the Prolog source file by use_module/1 and lib/1,2 if both files are present. On the other hand, the compile predicates expect a source file and will normally not load an object code file.

The compiler generates different object code depending on the settings of various pragmas. It is the settings of the pragmas at the time the object code is generated that determines what codes are generated, rather than at load time. The load time pragma settings have no effect on the object code that is loaded in, so for example, if the code was generated while the debug pragma is on, but loaded while the nodebug pragma is on, the loaded code is still the debuggable, non-optimised code.

Note that in addition to generating the object code for predicates found in the source file, fcompile also generates the object code of any auxiliary predicates that are called in the source file. These are the predicates that are generated by the compiler (such as the do/2 iterator). A warning is generated if a file contains more than one module. These warnings often indicates that files have been incorrectly omitted or included in the include directive.

fcompile/1,2 can be used to generate non-source versions of programs for delivery.

6.10.1  Restrictions

Currently, the compiler generates the auxiliary predicates for the do iterator using a global counter to name the predicates. Unfortunately this means that if an object file with auxiliary predicates is loaded into a module that already has existing code that contains auxiliary predicates, naming conflict can occur and the old auxiliaries may be replaced. It is thus strongly recommended that object files should not be loaded into an existing module. This will only be a problem if the file does not contain any module declarations that redefines the module (i.e. module/1), as these redefinition will erase the old copy of the module.

The predicate generates the object code by first compiling the program and then outputting the object code. Directives, which are executed in a normal compilation process, will not be executed during the output of the object code (but the directives themselves will be added to the object code so that they will be executed when the code is loaded). This can lead to differences between loading the object code and compiling the program if the directive affects the compiled code during the compilation (e.g. determining which files to load by a conditional in a directive).

If macro transformation is defined (via macro/3 declarations) in the module that is fcompiled, then the “protecting functor” no_macro_expansion (see section 12.2) should be used to prevent the macro definition itself from being transformed when the definition is generated by fcompile. For example:
:- local macro(no_marco_transformation(foo/1), trans_foo/2, []).
the no_macro_transformation/1 wrapper prevents this instance of foo/1 from being transformed when the directive is generated by fcompile. Note that this is only needed if all terms are transformed, and not for goals or clause transformation.

Object file portability

One restriction does apply between platforms of different word sizes: integers which fit in the word size of one platform but not the other are represented differently internally in ECLiPSe. Specifically, integers which takes between 32 and 64 bits to represent are treated as normal integers on a 64 bit machine, but as bignums (see section 8.2.1) on 32 bit machines. This difference is normally invisible, but if such numbers occur as constants in the program code (i.e. their values appear textually), they can lead to different low-level compiled abstract code on the different platforms. Avoid using such constants if you want the object code to be portable across different word sizes (they can always be computed at run-time, e.g. writing 2^34 instead of 17179869184).

Previous Up Next