Notes On The Design Of The Mercury Implementation

This file contains some information about the design of the whole Mercury implementation, in particular listing the different major subsystems and how we manage dependencies between different subsystems.

See also compiler_design.html for information on the design of the compiler subsystem.

Subsystems and subdirectories

The Mercury implementation is divided into several major subsystems. Each major subsystem is put in a different subdirectory.

In general, each subsystem is written in a single language; we prefer not to mix different languages in a single directory, and so if we plan to implement what is conceptually a single subsystem in two languages (as is the case for the Mercury debugger) then we generally divide that conceptual subsystem into different subdirectories for each language.

The subdirectories containing major subsystems are as follows:

In addition, there are some extra subdirectories for scripts and utility programs: These extra subdirectories provide the infrastructure and "glue code" that connects the major subsystems. There is also some additional infrastructure (the autoconf configuration stuff and the primary Makefile and Mmakefile) in the top-level directory.

As well as the subdirectories containing the major subsystems and the glue code, there are also some subdirectories that just provide documentation:

Finally there are some directories containing stuff that is for the developers of the Mercury implementation, rather than being part of the Mercury implementation:

Programs, shell scripts, and file names

Often executable programs in the Mercury implementation will need to access files provided by the Mercury implementation. However, we want to avoid hard-coding path names in the executables, and Unix does not provide any reasonable way for a program to determine what directory the executable file is in.

To solve this problem, executable programs which need to know path names are never invoked directly. Instead, we always write a small shell script that acts as a front-end for the executable program (e.g. scripts/mmc is the front-end for compiler/mercury_compile). The hard-coded path names get put in the shell script, which passes them on to the program as parameters or environment variables. The shell script is itself automatically generated from a template (e.g. scripts/mmc.in) that contains symbolic names of the form @foo@; the top-level `configure' script fills in the values for these based on the user-specified parameters to the configure script. The configure script is itself generated by `autoconf' from `configure.ac'.

Libraries and dependencies

Most major subsystems (which doesn't include `extras') get compiled to a single library or executable program, though a few, such as deep_profiler, get compiled to several executables. None get compiled to more than one library.

On most systems, mutual recursion between libraries is not very well supported. On Unix, for static linking you need to list such libraries more than once on the command line. And on Windows, allowing mutual recursion between different DLLs requires some fairly major contortions.

To avoid such difficulties, and for the sake of portability to future systems which may impose similar requirements, it is a design principle of the Mercury implementation that there should be no mutual recursion between libraries.

The Mercury linker links the different components that make up a program in the following order:

To avoid circularities, libraries cannot contain direct calls to any routines that are defined in libraries (or object files) that occur earlier in the above list. Any such calls must be made into indirect calls via function pointers. These function pointers can be initialized by the auto-generated init file, which, since it is at the start of the list, can refer to functions in any of the other components.