x86m2 - a complete modula-2 compiler conforming to PIM-2 Edition 2
x86m2 [-l] [-g] [-ng] [-p] [-bounds] [-students] [-Odynamic] [-Oi586] [-Oi686] [-On] [-O] [-Ouncalled] [-Omemcopy] [-nomath] [-M2RTS] [-m] [-S] [-16|-32] [-i386|-apu] [-gas|-masm|-bas] [-dryrun] [-v] [-o destinition] [-orderfile yourfile] [-orderonly] [-quiet] [-verbose] [-statistics] [-pedantic] [-M SearchPath] ModuleName
x86m2 is the driver program to a modula-2 compiler targetting 16 or 32 bit x86. It takes a number of arguments and invokes many subcomponents to build, assemble and link a modula-2 application. Many of the arguments given to x86m2 are passed through to the subcomponents. For example in the synopsis given above the -g and the -quiet onwards are passed through to the main compiler component. However the purpose of x86m2 is to shield the user of knowing the interaction of the subcomponents by presenting a simple interface.
There are two useful environment variables that may be used with the modula-2 utilities, these are M2PATH and M2OPTIONS. The M2PATH can be set to determine the directory order that the compiler will search to find library modules. The path is specified by a path, a space and more paths ie:
export M2PATH="/user/bloggs/m2/comp/libs /usr/m2/libs"
This indicates
that the compiler will search the directory
/user/bloggs/m2/comp/libs before it searches /usr/m2/libs to
find library modules. Note that you need to explicitly place
a . in the M2PATH variable to make the
compiler search your current working directory. Note that
the default path for the compiler may vary depending upon
the target. To find out the default path type: x86m2 -apu
-h or x86m2 -gas -h or x86m2 -bas -h.
The M2OPTIONS environment variable can be set to
contain any of the non path related options. Thus whenever
the compiler is invoked without any command line
options these environment options are used. Note that if an
option is specified on the command line then
M2OPTIONS is ignored. If the -M command line
option is used then it too will override the M2PATH
environment variable.
This mechanism allows users to configure their environment and not have to tediously specify for every compilation the options required.
|
-l |
link the module specified on the command line. The module is assumed to have already been compiled. |
-nomath
tell the compiler that you do not expect to use the coprocessor. The compiler will generate an error if any source code attempts to generate coprocessor instructions. This option is useful for realtime systems which are targetted for processors without floating point instructions.
|
-M2RTS |
do not automatically link with the module M2RTS. You should not specify this flag for normal UNIX applications. It might be appropriate to use this flag for a realtime system. (The linker will automatically include the module M2RTS even if no imports are specified in your program module. The UNIX environment requires M2RTS as HALT and some termination code is implemented inside this module). | ||
|
-m |
at link stage display all the modules that are to be linked. | ||
|
-g |
compile or link generating GNU GDB compatible debugging information. Note that occasionally the line number debugging information is not absolutely correct if the -O option is also supplied. Most of the statements correspond with the line numbers but procedure calls at the end of an IF THEN END statement sequence maybe misaligned by a few source lines. The -O option removes dead code and sometimes rearranges code sequences. | ||
|
-ng |
do not generate GNU GDB compatible debugging information. This option will override a -g found in the environment. |
-bounds
generate code to check the bounds of subranges and array indexes. The default is that this flag is FALSE.
-return
generate code to check that functions always exit with a RETURN and do not fall out at the end. The default is that this flag is FALSE.
|
-p |
generate profiling information. When the final binary is executed a profile trace is generated and this can be interpreted by the command gprof. | ||
|
-O |
switch on all code optimization within the compiler. | ||
|
-Ocse |
turn off common subexpressions optimization. This is useful if you are debugging source code as this optimization does not preserve line number ordering of assignments. (Perhaps this is a bug?) | ||
|
-Oi586 |
turn on pentium (i586) processor optimizations, such as pentium instructions and exploit the pentium pipeline. This can considerably improve the speed of the generated code. It enables efficient and inteligent pipelining, which understands cache lines, operand conflicts. It also aligns data on word and 16 byte boundaries where appropriate. can be used in conjunction with -Odynamic. This has just been implemented and has undergone limited testing (used on the compiler and not much else). Only use if desperate for speed. This flag tells the compiler to schedule i586 instructions to exploit the pipeline. | ||
|
-Oi686 |
turn on pentium (i686) processor optimizations. Currently this only enables the 686 cache line model and i586 optimizations. |
-Ocache
turn on cache optimizations.
-Oworst
tries to generate the worst code for the pipeline. Hopefully only used for testing out potential speed ups.
-Ouncalled
remove all procedures which are never called. Note that this may have no effect on the performance of the remaining code. Although it may have a secondary performance improvement as the binary might be smaller and thus consume less operating system resources.
-Odynamic
use the dynamic programming code generator. Produces much better code, as with most optimization techniques the debugging information becomes vague. In particular line numbers will not exactly match the code generated. Only use if you are desperate for speed. This flag will eventually be enabled with the -O option once the code generator has been thrashed through many different styles of code. Do not use this flag with the -16 flag.
|
-On |
tells the pipeline optimizer to find the optimal pipeline scheduling ordering for the next 3*n instructions. Default is -O2. Increasing beyond 4 causes compilation time to noticably increase. |
-Omemcopy
use the memcopy instruction for pushing and popping data from the stack. This optimization flag is only appropriate for the -apu processor. The default is not to use the flag.
|
-S |
compile the module specified on the command line, only generate an assembly language file. | ||
|
-16 |
generate 16 bit code and data. | ||
|
-32 |
generate 32 bit code and data - the default. | ||
|
-gas |
generate GNU GAS assembler syntax. | ||
|
-masm |
generate Intel MASM assembler syntax. | ||
|
-bas |
generate Bruce Evans assembler syntax. (Currently this only works with the -16 flag). | ||
|
-apu |
generate APU (Abtract Processor Code) machine language code. | ||
|
-i386 |
generate Intel i386 processor code. |
-dryrun
show what subcomponents will be invoked by x86m2, do not perform any compilation or linkage.
|
-v |
show the subcomponents that are invoked before actually invoking them. |
-o destinition
place the output of the compiler into file destinition.
-orderfile yourfile
the initialization order is specified by yourfile.
-orderonly
only create the order file, the name of which may have been specified by the previous -orderfile option.
|
-quiet |
do not display any Pass 1, Pass 2, Pass 3, Pass 4 messages, frequently found on modula-2 compilers. |
-verbose
if an error occurs the erroneous text is displayed with an arrow indicating the approximate character in error. The line number and file is always displayed, but sometimes the compiler attributes the error to the following line to the error. The -verbose option is more accurate and more verbose!
-statistics
generates quadruple information: number of quadruples generated, number of quadruples remaining after optimization.
-students
checks for novice peculiarities. Note that these warnings are not the same as error messages. Hardened users of Modula-2 may wish to ignore this option as it checks to see whether a user has declared same named variables in nested scopes and whether a variable looks similar to a keyword (case difference etc). These are allowed in Modula-2 but could be considered poor programming style.
-pedantic
forces the compiler to reject nested WITH statements referencing the same record type. Does not allow multiple imports of the same item from a module. It also checks that: procedure variables are written to before being read; variables are not only written to but read from; variables are declared and used. If the compiler encounters a variable being read before written it will terminate with a message. It will check that FOR loop indices are not used outside the end of this loop without being reset.
-M SearchPath
tells the compiler which directories to search, to find library modules. Typically one might specify -M "/user/bloggs/lib /user/m2/lib" to indicate that the directory /user/bloggs/lib should be searched first, and later /user/m2/lib should be searched if the required library module was not found in /user/bloggs/lib.
The module name should be the exact module name, NOT the (possibly truncated) file name.
The compiler generates error messages compatible with the emacs next error system.
The compiler gives substantial help in certain annoying modula-2 errors. For example it will list all the undeclared identifiers at the end of a compilation, it also lists all exported identifiers which have not been implemented, it will check parameters declarations carefully to see that the same types and names are used in the definition module and implementation module. Also, provided the -verbose option is used, the compiler will display any procedure parameters which conflict with the definition. The compiler will display any procedure parameters which conflict with declaration and give fairly precise details about where the conflict occurred.
The compiler allows abstract data types to be ANY type, not just restricted to a pointer type. It also allows declarations in any order (type declarations may be in any order the software developper desires, not the way the compiler desires). So for example constants and enumerated types may be declared at the bottom of a module etc.
There is an example of how to interface modula-2 in the library directory. The modula-2 compiler can be configured to push parameters from right to left (the default) or alternatively from left to right. To alter the default you need to alter the BOOLEAN constant in M2Configure.def and make.rules.
An example of interfacing modula-2 to C is contained within the interface file libc.def and libc.c.
The system module for this compiler really exists rather than being a pseudo module as in many modula-2 compilers. The compiler internally declares ADDRESS, WORD, BYTE, ADR, TSIZE, SIZE and these are exported by SYSTEM.def. The module system is actually parsed by the compiler and therefore one can add functions like IOTRANSFER, TRANSFER, PROCESS, NEWPROCESS.
In addition to the base types normally found in Modula-2 compilers the following types are also implemented: LONGINT and LONGREAL. These types are 64 bits in length. The module MATH exports trigonometrical functions such as sin, cos and tan which when compiled produce inline code.
Please email the author, gaiusmod2@gmail.com with any queries, suggestions, improvements, grumbles etc. A great debt of thanks goes to:
- Stuart Lewis
and all his budding first year programmers who identified so
many bad error messages, identified so many warning
improvements and for finding bugs/limitations in various
library modules.
- Keith Verheyden for test driving many of the alpha
releases.
without which the compiler would not be nearly as useful.
Source is available from: https://github.com/gaiusm/x86m2 The compiler is held under the GPL and the libraries are under LGPL.