This document describes each of the tools in the SRC Modula-3 distribution and how to use
them. Briefly, the tools include a compiler,
a linker, a pretty printer, and a
line-based profiler.
See also chapter~\ref{local} for tools and hints that are
local to your installation.
Compiling
To compile a Modula-3 program, invoke m3(1)
. This driver
is much in the style of cc(1)
; the output
is an object file, a library or an executable program, according to
the options.
m3
parses the command line and invokes
the compiler and linker as required. m3
tells the
compiler where to seek imported interfaces and where to find the
Modula-3 runtime library. Arguments ending in .m3
or
.i3
are assumed to name Modula-3 source files to be
compiled. Arguments ending in .mo
, .io
or
.o
are assumed to name object files, possibly created by
other language processors, that are to be linked with the object files
created by m3
. Arguments ending in .mc
,
.ic
, or .c
are assumed to name C source
files to be compiled. Arguments ending in .ms
,
.is
, or .s
are assumed to name assembly
language files to be translated into object files by the assembler.
Arguments starting with -
specify compiler options.
Other arguments are assumed to name library files, and are simply
passed along to the linker.
The source for a module named M
is normally in a file
named M.m3
. The source for an interface named
I
must be in a file named I.i3
. The main
program is the module that implements the interface
Main
.
There are options to compile without linking, stop compiling after
producing C, emit debugger symbols, generate profiling hooks, retain
intermediate files, override search paths, select non-standard
executables for the various passes, and pass arguments to individual
passes. For the full details, see the m3
(1) man page.
In a source file, an occurrence of IMPORT Mumble
causes
the compiler to seek an interface named Mumble
. The
compiler will step through a sequence of directories looking for the
file Mumble.i3
. It will parse the first such file that
it finds, which is expected to contain an interface named
Mumble
. If no file Mumble.i3
exists, or if
the parse fails, the compiler will generate an error. The particular
sequence of directories to be searched is determined by the options
passed to m3
. See the m3(1)
manual page for
full details.
In the file Main.m3
:
MODULE Main; IMPORT A; BEGIN A.DoIt (); END Main.In the file
A.i3
:
INTERFACE A; PROCEDURE DoIt (); END A.In the file
A.m3
:
MODULE A; IMPORT Wr, Stdio; PROCEDURE DoIt () = BEGIN Wr.PutText (Stdio.stdout, "Hello world.\n"); Wr.Close (Stdio.stdout); END DoIt; BEGIN END A.If SRC Modula-3 is installed correctly, the command
m3 -make -why -o hello Main.m3 A.m3 A.i3will compile the three compilation units and link them with the standard libraries. The result will be left in the executable file named
hello
.
m3build(1)
, a
replacement for plain make(1)
. The primary benefit
provided by m3make
is that the operational description
found in most makefiles
is replaced by a more declarative
one. The result is that makefiles
are smaller, simpler,
and more portable. You're not required to use m3make
,
but we believe you will like it.
The m3makefile
for the example above would be:
implementation (Main) module (A) program (hello)
BITS n FOR T
is treated as T
everywhere except when applied to a field in a record. In that case, the
field is implemented by a {\em bitfield} of width n
in a C
struct
. Otherwise, a \modula field is implemented as a {\em member} of
a C struct
. Consequently, \modula types that would require the C field
to span word boundaries are not accepted by SRC Modula-3.
\subsection*{Stack overflow checking.}
\index{overflow!stacks}
\index{thread!stacks}
SRC Modula-3 does not reliably detect thread stack overflows. Stacks
are only checked for overflow on procedure entry. No checking is done
on external procedures. Thread stacks are allocated in fixed size
chunks. The required Thread
interface has been augmented with
the SizedClosure
type to allow arbitrary sized stacks. The default
size can be adjusted with Thread.MinDefaultStackSize
and
Thread.IncDefaultStackSize
.
\subsection*{Exception semantics.}
\index{exceptions}
\index{exceptions!setjmp, longjmp@{\tt setjmp}, {\tt longjmp}}
\index{setjmp@{\tt setjmp}}
\index{longjmp@{\tt longjmp}}
SRC Modula-3 uses C's {\tt setjmp}/{\tt longjmp} mechanism to
unwind the stack when raising an exception. A problem can
occur: assignments may appear to be undone.
For example, consider
TRY i := 3; P (); EXCEPT E: j := i; END;where {\tt P} raises exception {\tt E}. The compiler generates a {\tt setjmp} at the beginning of the try statement. If the C compiler allocates variable {\tt i} to a register, the assignment of {\tt 3} may be lost during the {\tt longjmp} and branch that gets to the handler. \subsection*{Method constants.} The language definition says that if {\tt T} is an object type and {\tt m} one of its methods, {\tt T.m} denotes the procedure value that implements that method and that this value is a constant. In SRC Modula-3, {\tt T.m} denotes the correct procedure constant, but since the compiler generates runtime code to locate the method, some uses of the constant that the {\tt C} compiler must resolve at link time will cause {\tt C} errors. For example,
CONST P = T.m; BEGIN P (...) ...will work, since no initialized {\tt C} storage is allocated for {\tt P}. But the following generates initialized storage and will fail
CONST X = ARRAY [0..2] OF Proc { T.m, ..};Similarly, although Modula-3 allows it, the following cannot be evaluated at compile time
CONST X = (T.m = MyProcedure);\section{Pragmas} %================ \index{pragmas} SRC Modula-3 recognizes the pragmas described below. \subsection*{\tt <*EXTERNAL*>} \index{external linkage} \index{C linkage@{\tt C} linkage} \index{<*EXTERNAL*>@{\tt <*EXTERNAL*>}} The pragma
<*EXTERNAL
N:
L*>
may precede an interface
or a procedure or variable declaration in an interface. It asserts
that the following entity is named ``N'' and implemented in language ``L''.
If ``N'' is omitted, the external name is the \modula name. The default
and only recognized value for ``L'' is C
. The ``:
'' is only
required when specifying ``L''. ``N'' and ``L'' may be
\modula identifiers or string literals.
The names of external procedures and variables are passed through
to the C compiler unchanged. {\em The types of external variables, the
types of formal parameters, the types of results, and the raises
clauses of external procedures are all assumed to be correct and
are not checked
against their external implementation.} Standard calling conventions
are used when calling external procedures.
Beginning an interface with <*EXTERNAL*>
declares all of the
procedures and variables in that interface external.
For example:
<*EXTERNAL*> INTERFACE OS; VAR errno: INTEGER; PROCEDURE exit (i: INTEGER); END OS.allows importers of
OS
to access the standard \unix symbols
errno
and exit
through the names OS.errno
and
OS.exit
respectively.
Alternatively, the following interface provides access to
the same two symbols, but uses a more conventional \modula name
for the procedure:
INTERFACE OS; <*EXTERNAL errno:C *> VAR errno: INTEGER; <*EXTERNAL exit:C *> PROCEDURE Exit (i: INTEGER); END OS.If several variables are declared within a single
<*EXTERNAL*> VAR
declaration, they are all assumed to be external.
The external pragma may optionally specify a name different from the
\modula name.
For example:
INTERFACE Xt; <*EXTERNAL "_XtCheckSubclassFlag" *> PROCEDURE CheckSubclassFlag (...); ...defines a procedure named
Xt.CheckSubclassFlag
in \modula
and named _XtCheckSubclassFlag
in the generated C.
\subsection*{\tt <*INLINE*>}
\index{<*INLINE*>@{\tt <*INLINE*>}}
The pragma <*INLINE*>
may precede a procedure
declaration. The pragma is allowed in interfaces and modules.
SRC Modula-3 recognizes but ignores this pragma.
For example:
INTERFACE X; <*INLINE*> PROCEDURE P (i: INTEGER); <*INLINE*> PROCEDURE Q (); END X.declares
X.P
and X.Q
to be inlined procedures.
\subsection*{\tt <*ASSERT*>}
\index{<*ASSERT*>@{\tt <*ASSERT*>}}
\index{assertions}
\index{correctness}
The pragma <*ASSERT
expr*>
may appear anywhere a statement
may appear. It is a static error if ``expr'' is not of type BOOLEAN
.
At runtime ``expr'' is evaluated. It is a checked
runtime error if the result is FALSE
.
Assertion checking can be disabled with the -a
compiler
switch.
\subsection*{\tt <*TRACE*>}
\index{<*TRACE*>@{\tt <*TRACE*>}}
The pragma <*TRACE
expr*>
may appear at the end of any variable
or formal declaration. This pragma will generate tracing calls whenever
the declared variable is modified.
The ``expr'' must evaluate to a procedure of two arguments.
The first argument is the name of the traced variable, a TEXT
.
The second argument is the traced variable.
Note that any of the formal passing modes may be used with the
second argument.
For example:
MODULE M; VAR x: Foo <*TRACE MyTrace.FooChanged*>;will cause
MyTrace.FooChanged ("M.x", x)to be generated after each statement that modifies x. Variable aliasing is not tracked, so
WITH alias = x DO INC(alias) ENDwill not generate any tracing. The pieces of Modula-3 grammar affected by
<*TRACE
expr*>
are:
VariableDecl = IdList (":" Type & ":=" Expr) V_TRACE. Formal = [Mode] IdList (":" Type & ":=" ConstExpr) V_TRACE. ForSt = FOR Id V_TRACE ":=" Expr TO Expr [BY Expr] DO S END. Handler = QualId {"," QualId} ["(" Id V_TRACE ")"] "=>" S. TCase = Type {"," Type} ["(" Id V_TRACE ")"] "=>" S. Binding = Id V_TRACE "=" Expr. V_TRACE = [ "<*" TRACE Expr "*>" ].The pragma
<*TRACE
stmt-list*>
may appear immediately after any
BEGIN
. The specified ``stmt-list'' will be inserted after each
statement of the block started by the BEGIN
. For example:
BEGIN <* TRACE INC(cnt); MyTrace(cnt) *> i := j; j := i; END;will generate
INC(cnt); MyTrace(cnt)
after each of
the assignment statements.
\subsection*{\tt <*FATAL*>}
\index{<*FATAL*>@{\tt <*FATAL*>}}
\index{exceptions!lint}
The pragma <*FATAL
id-list*>
may appear anywhere a
declaration may appear. It asserts that the exceptions named
in ``id-list'' may be raised, but unhandled in the containing scope.
If they are, it's fatal and the program should crash. Effectively,
the <*FATAL*>
pragma disables a specific set of
``potentially unhandled exception'' warnings. If ``id-list'' is ANY
,
the pragma applies to all exceptions. The effects of the
<*FATAL*>
pragma are limited to its containing scope ---
they cannot be imported from interfaces.
For example:
EXCEPTION InternalError; <*FATAL InternalError*>at the top-level of a module
M
means that no warnings will be
generated for procedures in M
that raise but don't list
InternalError
in their RAISES
clauses.
Similarly,
PROCEDURE X() RAISES {} = BEGIN ... <*FATAL ANY*> BEGIN List.Walk (list, proc); END; ... END X;specifies that although
X
raises no exceptions and List.Walk
may, no warnings should be generated.
\subsection*{\tt <*UNUSED*>}
\index{<*UNUSED*>@{\tt <*UNUSED*>}}
\index{unused symbols}
The pragma <*UNUSED*>
may precede any declaration. It asserts
that the entity in the following declaration is not used and no
warnings should be generated.
For example, the procedures that implement the default methods
for an object may not need all of the actual parameters:
PROCEDURE DefaultClose (<*UNUSED*> wr: Wr.T) = BEGIN (* do nothing *) END DefaultClose;\subsection*{\tt <*OBSOLETE*>} \index{obsolete symbols} \index{<*OBSOLETE*>@{\tt <*OBSOLETE*>}} The pragma
<*OBSOLETE*>
may precede any declaration
(e.g. <*OBSOLETE*> PROCEDURE P ();
). A warning is emitted in
any module that references an obsolete symbol. This feature is used
to warn clients of an evolving interface that they are using
features that will disappear in the future.
\subsection*{\tt <*NOWARN*>}
\index{<*NOWARN*>@{\tt <*NOWARN*>}}
\index{warnings}
\index{compiler!warnings}
The pragma <*NOWARN*>
may appear anywhere. It prevents warning
messages from being issued for the line containing the pragma. It is
probably better to use this pragma in a few places and enable
all warnings with the -w1
switch than to ignore all warnings.
\subsection*{\tt <*LINE*>}
\index{line numbers}
\index{<*LINE*>@{\tt <*LINE*>}}
For the benefit of preprocessors that generate \modula programs, the
compiler recognizes a <*LINE ... *>
pragma, in two forms:
<*LINE number filename *> <*LINE number *>where \verb+number+ is an integer literal and \verb+filename+ is a text literal. This pragma causes the compiler to believe, for purposes of error messages and debugging, that the line number of the following source line is \verb+number+ and that the current input file is \verb+filename+. If \verb+filename+ is omitted, it is assumed to be unchanged.
<*LINE ... *>
may appear between any two Modula-3 tokens; it
applies to the source line following the line on which it appears.
Here's an example: \verb+<*LINE 32 "SourceLoc.nw" *>+.
\subsection*{\tt <*PRAGMA*>}
\index{<*PRAGMA*>@{\tt <*PRAGMA*>}}
The pragma <*PRAGMA
id-list*>
may appear anywhere. It notifies
the compiler that pragmas beginning with the identifiers in ``id-list''
may occur in this compilation unit. Since the compiler is free to
ignore any pragma, the real effect of <*PRAGMA*>
is to tell
the compiler that pragmas it doesn't implement are coming, but they
shouldn't cause ``unrecognized pragma'' warnings.
\section{Linking}
%================
\index{linking}
SRC Modula-3 requires a special two-phase linker. You must link
\modula programs with m3
.
The first phase of the linker checks that all version stamps are
consistent, generates flat struct*
declarations for all
opaque and object types, and builds the initialization code from the
collection of objects to be linked. The second phases calls {\tt ld}
to actually link the program.
\index{files!suffixes}
\index{files!.ix, .mx, .ax@{\tt .ix}, {\tt .mx}, {\tt .ax}}
\index{.ix, .mx, .ax files@{\tt .ix}, {\tt .mx}, {\tt .ax} files}
The information needed by the first phase is generated by the compiler
in files ending in .ix
and .mx
. Libraries containing
\modula code must be created using m3 -a
. m3
will
combine the .ix
and .mx
files for the objects in the
library into a new file ending in .ax
. The .ix
, .mx
,
and .ax
files must reside in the same directory as their corresponding
.io
, .mo
and .a
files. If m3
encounters
a library without a .ax
file, it assumes that the library contains
no \modula code.
\index{version stamps}
\index{safe linkage}
For every symbol X.Z
exported or imported by a module, the
compiler generates a version stamp. These stamps are used to ensure that
all modules linked into a single program agree on the type of X.Z
.
The linker will refuse to link programs with inconsistent version stamps.
\section{Runtime arguments}
%==========================
\index{runtime arguments}
\index{M3 arguments@{\tt "@M3} arguments}
\index{showheap@{\tt "@M3showheap}}
\index{M3showheap@{\tt "@M3showheap}}
\index{showthread@{\tt "@M3showthread}}
\index{M3showthread@{\tt "@M3showthread}}
\index{nogc@{\tt "@M3nogc}}
\index{M3nogc@{\tt "@M3nogc}}
\index{noincremental@{\tt "@M3noincremental}}
\index{M3noincremental@{\tt "@M3noincremental}}
\index{nogenerational@{\tt "@M3generational}}
\index{M3nogenerational@{\tt "@M3generational}}
\index{novm@{\tt "@M3novm}}
\index{M3novm@{\tt "@M3novm}}
\index{nogc@{\tt "@M3nogc}}
\index{M3nogc@{\tt "@M3nogc}}
\index{paranoidgc@{\tt "@M3paranoidgc}}
\index{M3paranoidgc@{\tt "@M3paranoidgc}}
Command line arguments given to Modula-3 programs are divided in two
groups. Those that start with the characters @M3
are reserved
for the \modula runtime and are accessible via the RTParams
interface (we call those the {\em runtime parameters}). The others are
accessible via the Params
, ParseParams
, and RTArgs
interfaces (these are the {\em program arguments}).
The following runtime parameters are recognized today;
others are simply ignored.
\begin{itemize}
\item @M3nogc
turns the garbage collector off.
\item @M3showheap=
{\it name} activates the logging of
heap allocation and garbage collection events. The program forks a
process running the {\it name} program, and sends it these events as
they occur. If =
{\it name} is ommitted, the {\tt showheap}
program is forked (it is part of the {\tt tools} archive); this
program displays the status of the heap pages. See its man page for
more details.
\item @M3showthread=
{\it name} activates the logging of
thread switching events. The program forks a
process running the {\it name} program, and sends it these events as
they occur. If =
{\it name} is ommitted, the {\tt showthread}
program is forked (it is part of the {\tt tools} archive); this
program displays the status of the various threads. See its man page
for more details.
\item @M3noincremental
disables incremental garbage collection;
uses stop-and-copy instead.
\item @M3nogenerational
disables generational garbage collection.
\item @M3novm
disables the use of VM protection by the garbage
collector. Implies @M3noincremental
and @M3nogenerational
.
\item @M3paranoidgc
checks the heap for sanity after each collection.
\end{itemize}
\section{Garbage Collection}
%===========================
\index{garbage collection}
\index{garbage collection!copying}
\index{collector, copying}
\index{hashing REFs@hashing {\tt REF}s}
A crucial fact for clients of the garbage collector to know is that {\em
objects in the heap move}. If all references to a traced heap object
are from other traced heap objects, the collector may move the
referent. Hence, it is a bad idea to hash pointer values. References
from the stack or untraced heap into the traced heap are never
modified.
The new collector is, by default, incremental and generational.
The interruptions of service should be very small, and the overall
performance should be better than with the previous collectors.
The use of VM protection is currently implemented only for the DS3100
architecture. On other architectures, @M3novm
is the default.
Note that the new optional background collection
thread is not on by default; this may change in the future.
The procedures RTHeap.DisableCollection
and
RTHeap.EnableCollection
are now marked as obsolete,
although they still work; preferred new
alternatives are described in the \intf{RTHeap} interface.
\index{debugging}
\index{gdb@{\tt gdb}}
\index{bus error}
\index{VM faults}
When you debug a Modula-3 program, you might find it simplest to
run it with the @M3novm
switch. For example, if you use gdb
,
you can start your program with
(gdb) run MyProgram @M3novmIf you do not use the
@M3novm
flag, you must set gdb
to ignore VM faults generated on the collector's behalf, by typing
(gdb) handle 11 noprint passAnd then, without
@M3novm
, you might not be able to examine the heap
when the program stops, because the heap may be protected. You can
turn off all current heap protection by telling gdb
(gdb) call RTHeap__FinishVM()If you also want the collector not to use VM protection in the future (i.e., if you wish you'd typed
@M3novm
to start with), you can type
(gdb) call RTHeap__NoVM()If your program is not run from the debugger, and it dumps core, the runtime automatically calls the equivalent of a
RTHeap.FinishVM()
to let you examine the heap in the core file.
\index{sigvec@{\tt sigvec}}
\index{fork@{\tt fork}}
\index{vfork@{\tt vfork}}
Because of the use of VM protection by the collector,
there are some additional constraints
on what programs may legally do. For example, you cannot pass an
address on the heap as an argument to sigvec
(2). These restrictions
are documented in \intf{RTHeapDepC.c}.
If they seem onerous, we might be able to eliminate some. Note also
that fork()
and vfork()
are now relatively expensive operations,
since they cause the current collection to finish; this situation may improve
in a future release.
\section{Debugging}
%==================
\index{debugging}
\index{dbx@{\tt dbx}}
\index{gdb@{\tt gdb}}
Since an intermediate step of the \modula compiler is to produce C code,
you may use any debugger for which your C compiler can produce
debugging information; in most cases, this means {\tt dbx} or {\tt gdb}.
However, this mechanism has limitations: the C compiler
generates source-level information that relates the executable program
to the intermediate C code, not to the \modula source code.
We attempted to reflect as much as possible of the source-level \modula
information into the intermediate C code.
But there are still some shortcomings that you should know about.
\subsection*{Names}
%-----------------
\index{name mangling}
\index{debugging!names}
\index{names, in the debugger}
Global names (i.e. top-level procedures, constants, types, variables,
and exceptions) are prefixed by their module's name and two underscores.
For example, in an interface or module named X
, the C name of a
top-level procedure P
would be X__P
. Note, there are
two underscores between X
and P
.
Local names (e.g. of local variables and formal parameters) are preserved.
The compiler will issue a warning and append an underscore to any
\modula name that is a C reserved word.
\subsection*{Types}
%-----------------
\index{type names}
\index{t1fc3a882@{\tt \_t1fc3a882}}
\modula is based on structural type equivalence,
C is not. For this reason, the compiler maps all structurally
equivalent \modula types into a single C type. These C types have
meaningless names like _t1fc3a882
. The \modula type names are
equated to their corresponding C type. Unfortunately variables
are declared with the C type names. So, if you ask your debugger
``what is the type of v
?'', it will most likely answer,
``_t13e82b97
''. But, if you ask ``what is _t13e82b97
?''
it will most likely give you a useful type description.
The table~\ref{table:conversion} indicates the C types corresponding to
\modula types.
\begin{table}
\begin{center}
\begin{tabular}{p{1.3in}p{4.6in}}
\multicolumn{1}{c}{\modula} & \multicolumn{1}{c}{C}\\ \hline
enumeration &
unsigned char
, unsigned short
or unsigned int
depending
on the number of elements in the enumeration.
\\ \\
INTEGER
&
int
\\ \\
subrange &
char
, short
or int
, possibly
unsigned
, depending on the base type of the subrange.
Subranges of enumerations are implemented by the same type
as the full enumeration.
Subranges of INTEGER
are implemented by the smallest
type containing the range.
For example, the type [0..255]
is mapped to unsigned char
and [-1000..1000]
is mapped to short
.
\\ \\
REAL
&
float
\\ \\
LONGREAL
&
double
\\ \\
EXTENDED
&
double
\\ \\
$ARRAY I OF T
$ &
struct { tT elts[n] }
, where tT
is the C type
of T
and n
is NUMBER(I)
.
\\ \\
$ARRAY
^n OF T
$ &
$struct { tT* elts; int size[
n ] }
$, where tT
is the C type of T
and elts
is a pointer
to the first element of the array.
\\ \\
RECORD ... END
&
struct{ ... }
with the same collection of fields
as the original record.
\\ \\
BITS n FOR T
&
Usually tT
where tT
is the the C type of T
.
When T
is an ordinal type and the packed type occurs
within a record, it generates a C bit field.
\\ \\
SET OF T
&
struct { int elts[n] }
where n
is
$\lceilNUMBER (T)
/ sizeof(int)
\rceil$.
\\ \\
\begin{tabular}[t]{@{}l@{}}
REF T
\\
UNTRACED REF T
\\
\end{tabular} &
tT*
where tT
is the C type of T
.
\\ \\
OBJECT ... END
&
ADDRESS
, a {\tt typedef} for char*
or void*
(depending on the system) defined in M3Machine.h
.
Each use of an object reference is cast into a pointer
of the appropriate type at the point of use.
\\ \\
PROCEDURE (): T
&
Usually tT *(proc)()
where tT
is the C type of T
.
If T
is a record or array, an extra VAR
parameter
is passed to the procedure which it uses to store the return
result.
%% \\ \\
\end{tabular}
\end{center}
\caption{Type implementations}
\label{table:conversion}
\end{table}
\index{debugging!printing REFs@printing {\tt REF}s}
\index{REF!printing}
\index{type names}
Despite the fact that the compiler turns all object references
into char*
, the linker generates useful type declarations.
These declarations are available under the type's global name.
For example, to print an object o
of type Wr.T
,
type print *(Wr__T)o
. Note that if o
was really
a subtype of Wr.T
, say TextWr.T
, then you must
use print *(TextWr__T)o
to see the additional fields.
If the same type appears with two names in a program, the linker
arbitrarily picks one.
\index{debugging!printing TEXTs@printing {\tt TEXT}s}
\index{TEXT!printing}
To print the null terminated string in a variable of type TEXT
(or Text.T
) named txt
, type print *(char**)txt
.
\index{debugging!printing REFs@printing {\tt REF}s}
\index{REF!printing}
\index{typecodes}
\index{typecells}
\index{type names}
\index{runtime!type headers}
If you don't know the type of a traced reference, you may be
able to use the runtime information to discover it. Given a
reference r
, print *(_refHeader*)(((char*)r)-4)
will print its typecode x
, and print *_types[x]
will print the corresponding typecell. A typecell includes a
type's \modula name as a C string (typecell.name
). If
the type doesn't have a \modula name, its internal is the
concatenation of ``_t
'' and typecell.selfID
in hex.
\subsection*{File names and line numbers}
%---------------------------------------
Due to liberal use of the #line
mechanism of C, the \modula
file names and line numbers are preserved. Your debugger should give
you the right names and line numbers and display the correct \modula
source code (if it includes facilities to display source code).
Note that uses of the <*LINE*>
pragma are propagated into
the intermediate C code.
\subsection*{Debugger quirks}
\index{.dbxinit file@{\tt .dbxinit} file}
\index{dbxinit file@{\tt .dbxinit} file}
\index{SIGVTALRM@{\tt SIGVTALRM}}
\index{Unix!signals}
Most debuggers have a few quirks. dbx
is no exception.
We've found that having a .dbxinit
file in your home
directory with the following contents prevents many surprises:
ignore SIGVTALRM set $casesense = 1The first line tells
dbx
to ignore virtual timer
signals. They are used by the \modula runtime to trigger
thread preemptions.
The second line tells dbx
that your input is
case sensitive.
\subsection*{Procedures}
%----------------------
\modula procedures are mapped as closely as possible into C procedures.
Two differences exist: ``large'' results and nested procedures.
\index{structured function results}
\index{procedures!structured results}
\index{functions!see{procedures}}
First, procedures that return structured values (i.e. records, arrays
or sets) take an extra parameter. The last parameter is a pointer to
the memory that will receive the returned result. This parameter was
necessary because some C compilers return structured results by
momentarily copying them into global memory. The global memory scheme
works fine until it's preempted by the \modula thread scheduler.
\index{procedures!nested}
\index{nested procedures}
\index{environments, procedure}
\index{closures}
Second, nested procedures are passed an extra parameter. The
first parameter to a nested procedure is a pointer to the local
variables of the enclosing block. To call a nested procedure from the
debugger, pass the address of the enclosing procedure's local variable
named frame
.
When a nested procedure is passed as a parameter, the address of the
corresponding C procedure and its extra parameter are packaged
into a small closure record. The address of this record is actually
passed. Any call through a formal procedure parameter first checks to
see whether the parameter is a closure or not and then makes the
appropriate call. Likewise, assignments of formal procedure
parameters to variables perform runtime checks for closures.
<*EXTERNAL*>
procedures have no extra parameters.
{\em except if they return large results??}
\subsection*{Threads}
%-------------------
\index{debugging!threads}
There is no support for debugging threads. That is, there is no mechanism to
force the debugger to examine a thread other than the one currently
executing. Usually you can get into another thread by setting a breakpoint
that it will hit. There is no mechanism to run a single thread while keeping
all others stopped.
If your debugger allows you to call procedures in a stopped program,
as both dbx
and gdb
do, then print Thread__DumpEverybody()
will produce a table listing the status of all threads.
\section{Thread scheduling}
%==========================
\index{scheduling}
\index{threads!scheduling}
\index{threads!user level}
This version of SRC Modula-3 has a more flexible scheduling algorithm
than the previous versions. Here is a rough explanation of its
behaviour.
All threads are kept in a circular list. This list is
modified only when new threads are created or when threads exit; that
is, the relative order of threads in this list is never modified.
When the scheduler comes into action, the list of threads is scanned
starting with the thread following the one currently running, until
a thread that can execute is found:
\begin{itemize}
\item if it was preempted by the scheduler, it can execute
\item if it is waiting for a condition or a mutex that is still held,
it cannot execute
\item if it has blocked because of a call to {\tt Time.Pause} (or
a similar procedure), it can execute iff the timeout is now expired
\item if it has blocked because of a call to {\tt RTScheduler.IOSelect} (or
a similar procedure), it can execute iff the timeout is now expired
or a polling {\tt select(2)} returns a non-zero value.
\end{itemize}
If such a thread is found, it becomes active.
\index{Time.Pause@{\tt Time.Pause}}
\index{RTScheduler.IOSelect@{\tt RTScheduler.IOSelect}}
\index{select@{\tt select}}
\index{blocking, process}
\index{waiting, process}
If no thread can execute, and there are no threads blocked in a {\tt
Time.Pause} or a {\tt RTScheduler.IOSelect}, a deadlock situation is detected
and reported. Otherwise, a combination of the file descriptors sets
(OR of all the file descriptors sets) and timeouts (MIN of all the
timeouts) is formed, {\tt select(2)} is called with those arguments
and the whole process of searching for an executable thread is redone.
This ensure that the Unix process does not consume CPU resources while
waiting.
The scheduler is activated when the running thread tries to acquire a
mutex which is locked, waits for a condition, calls {\tt Time.Pause}
(or a similar procedure) with a future time, calls {\tt RTScheduler.IOSelect}
(or a similar procedure) with a non-zero valued timeout and no files
are ready at the time of the call, or the time allocated to the thread
has expired (preemption).
\index{threads!preemption}
\index{preemption}
Preemption is implemented using the Unix virtual interval timer. {\tt
RTScheduler.SetSwitchingInterval} can be used to change the interval
between preemptions. SRC Modula-3 no longer uses the real time interval
timer nor the profiling interval timer for thread scheduling; these
are available to the program.
\index{sigpause@{\tt sigpause}}
\index{select@{\tt select}}
Because of the preemption implementation, Unix kernel calls
will block the process (i.e. the Unix process sleeps even though
some threads could run). However, {\tt Time.Pause} and {\tt
RTScheduler.IOSelect} provide functional equivalents of {\tt sigpause(2)} and
{\tt select(2)} that do not cause the process to block.
\section{Profiling}
%==================
\index{profiling}
\index{prof@{\tt prof}}
\index{gprof@{\tt gprof}}
\index{pixie@{\tt pixie}}
In addition to the usual profiling tools (e.g. see prof(1)
,
gprof(1)
and pixie(1)
), SRC Modula-3 provides support
for line-based profiling.
\index{analyze coverage@{\tt analyze\_coverage}}
\index{coverage}
\index{profiling!line-based}
\index{-Z@{\tt -Z}}
To enable collection of data during the execution of programs, give the
-Z
option to the m3
command for the compilation of the
modules you want to examine and also for the linking of the program.
To interpret the result, run analyze_coverage(1)
.
Note that because of the extensive data collection performed by this mode of
profiling, the execution time of the program can be significantly larger when
it is enabled; thus, simultaneous time profiling can produce erroneous
results. For the same reason, the profiling data file is rather large;
furthermore, as it is augmented by each execution of the program, you may want
to compress it from time to time (see analyze_coverage(1)
for more
details).
\section{Pretty printing}
%========================
\index{m3pp@{\tt m3pp}}
SRC Modula-3 includes a pretty-printer for \modula programs.
It is accessible as m3pp
(1). Read its man page to find out how to use
it.
\section{Gnuemacs support}
%=========================
\index{.emacs@{\tt .emacs}}
\index{editting}
\index{gnuemacs@{\tt gnuemacs}}
\index{gnuemacs!Modula-3 mode}
\index{epoch@{\tt epoch}}
\index{emacs@{\tt emacs}}
\subsection{\tt modula-3-mode}
%-----------------------------
\index{gnuemacs!Modula-3 mode}
\index{command completion}
SRC Modula-3 comes with a mode for editing Modula-3 programs under
{\tt gnuemacs}. Here is a list of the key things this mode provides:
\begin{itemize}
\item Indenting/pretty-printing
\index{pretty-printing} Modula-3 mode gives you access to two methods
of formatting code, one ``batch'' and one ``interactive.'' The batch
method invokes the program m3pp
, which takes a program unit
such as a procedure and formats it completely. The gnuemacs
commands that invoke m3pp
are M-x m3::pp-buffer
which
pretty prints the current buffer, M-x m3::pp-region
which
pretty-prints the code between mark and point, and M-x
m3::pp-unit
which pretty-prints the ``unit'' containing the cursor.
(A unit is a top-level construct such as CONST
, TYPE
,
VAR
, or PROCEDURE
.) m3::pp-buffer
,
m3::pp-region
and m3::pp-unit
are bound to the keys
C-c b
, C-c r
and C-c u
, respectively.
The other method of formatting text is a more traditional one for
gnuemacs
, in which there the language mode provides a key that
indents the current line appropriately. In keeping with the
convention used in modes for other languages such as Lisp and C, the
key used is TAB
. Typing TAB
on a line indents the
current line in a way that is (we hope) appropriate given the lines
that precede it.
The two formatting methods are not mutually exclusive; perhaps
you like the way m3pp
lines up columns in declarations,
but you also like to keep things indented while you type. You
can use the electric mode to get things close, then invoke
m3pp
when you're done.
\item Avoidance of typing:
\index{electric Modula-3 mode}
Modula-3 mode offers some aid if you don't like typing a lot of
uppercase keywords. The TAB
actually serves double
duty; it not only indents the current line, but when invoked at
the end of a word, it attempts to complete the current word as a
keyword. For example b TAB
expands the b
to
BEGIN
, provided the b
appears in a context where
BEGIN
may be a valid keyword. There are some fairly extensive
rules governing the contexts in which a given keyword is a valid
completion; the net result is that it is seldom necessary to type
more than one letter to get the correct completion. If you
specify a non-unique prefix of a set of keywords, it chooses the
first in an ordering intended to capture frequency of use; it
also presents the other choices, and typing TAB
repeatedly
cycles through these choices.
A pair of related features are ``END
-completion'' and
``END
-matching.'' If the elisp variable
m3::electric-end
is set to 'all
, completing the
keyword END
has the additional effect of finding the
construct that the END
completes. If that construct is a
an interface, module, or procedure, it fills in the name of the
completed construct after the END
; otherwise, it inserts a
comment containing the keyword of the completed construct. If
m3::electric-end
is 'proc-mod
, it only fills in
real names, never keyword comments. Independently, a non-nil
value of the elisp variable m3::blink-end-matchers
causes
completion of END
to blink the cursor briefly at the
beginning of the completed construct.
\item Finding files.
\index{mpindex}
\index{interfaces, finding}
\index{browsing interfaces}
The key C-c i
is bound to m3::show-interface
, which
expects the point to be in an interface name, and attempts to
find that interface and display it in another window. (If you
are using epoch
and the value of
m3::show-file-new-screen
is t
, which is the default,
the interface will be displayed in a new epoch
screen,
that is, a top-level X window. If you use m3-path
files,
m3::show-interface
will use those to provide a search
path; otherwise, it uses a built-in list of directories. The
default value of this list is the one used at SRC; other
sites will probably need to modify this.
The key C-c m
is bound to m3::show-implementation
. This
attempts to find the module that implements the interface in the
current buffer. This function relies on a SRC-specific
convention where public interfaces are symbolic links to home
directories for the packages that export them, which also contain
the implementations. Obviously, this site-specific convention
may not work outside of SRC, and m3::show-implementation
may need to be re-implemented or abandoned.
\end{itemize}
To have the Modula-3 mode automatically invoked when visiting a
Modula-3 source file, you should put in your .emacs
:
(autoload 'modula-3-mode "modula3") (setq auto-mode-alist (append '(("\\.ig$" . modula-3-mode) ("\\.mg$" . modula-3-mode) ("\\.i3$" . modula-3-mode) ("\\.m3$" . modula-3-mode)) auto-mode-alist))It is also convenient to have the lines:
(setq completion-ignored-extensions (append '(".mo" ".mx" ".mc" ".io" ".ix") completion-ignored-extensions))so that you don't get the files with those extensions offered as possible completions. Your system administrator may have inserted these lines in the default macro files for your system. \subsection{Tags} %---------------- \index{gnuemacs!tags} \index{m3tags@{\tt m3tags}} \index{tags} There is also a program to build tags file for \modula programs:
m3tags
; see the manpage for the details. When the system is
installed, a tag file for the public interfaces is built. To access
it, you need in your .emacs
(or in the system initialization file)
the line:
(visit-tags-table "LIB_USE/FTAGS")where
LIB_USE
is the place where the \modula libraries have
been installed.
\section{Unix signals}
%=====================
\index{Unix!signals}
\index{signals, Unix}
On Unix the Modula-3 runtime catches three signals: SIGSEGV
,
SIGBUS
, and SIGVTALRM
. Otherwise, the runtime leaves
the default Unix signal handlers unaltered.
\index{SIGSEGV@{\tt SIGSEGV}}
\index{segmentation violation}
SIGSEGV
indicates a ``segmentation violation'' and is often
signaled when a process dereferences NIL
.
The runtime catches SIGSEGV
, prints an error message, and attempts
to crash with a ``core file''.
\index{bus error}
\index{SIGBUS@{\tt SIGBUS}}
SIGBUS
indicates a ``bus error'' (pdp-11 days?) and is often signaled
when the process accesses unmapped memory (usually a thread stack overflow).
The runtime catches SIGSEGV
, prints an error message, and attempts
to crash with a ``core file''.
\index{SIGVTALRM@{\tt SIGVTALRM}}
\index{threads!preemption}
\index{timers}
SIGVTALRM
is the ``virtual timer alarm''. It is used to periodically
preempt the running thread.
\section{Keeping in touch}
%=========================
\index{comp.lang.modula3@\newsgroup}
\index{Usenet}
\index{news group}
\index{m3-request@{\tt m3-request"@src.dec.com}}
\paragraph{\newsgroup\relax} is a Usenet newsgroup devoted to \modula.
There you will find discussions on the language and how to use it,
annoucements of releases (both of SRC Modula-3 and of other systems). Since not
everybody has access to Usenet, we also maintain a relay mailing list, to which we
resend the articles appearing in \newsgroup. To be added to this list, send a
message to m3-request@src.dec.com
. You may post articles to
\newsgroup by sending them to m3@src.dec.com
.
\index{bug reports}
\paragraph{Reporting bugs.} We prefer that you send bug reports to
m3-request@src.dec.com
. After we have reviewed your report, we may post
an article in \newsgroup, describing the bug and a workaround or a fix.
Needless to say, this implementation probably has many bugs. We are
quite happy to receive bug reports. We can't promise to fix them, but
we will try. When reporting a bug, please send us a short program that
demonstrates the problem.