previous next top contents index framed top this page unframed
17.1. Optimization
Optimization causes the compiler to use a variety of code improvement
strategies to emit better code for MAINSAIL objmods.
Optimized code takes longer to compile than non-optimized code
and cannot be debugged with MAINDEBUG,
but usually runs faster, sometimes substantially faster.
Optimization may be governed by compiler subcommands (see the Chapter 4 of the MAINSAIL Compiler User's Guide) or, with finer control, by directives in a source MODULE. In most cases, it is sufficient to specify optimization at the per-MODULE level with the compiler's OPTIMIZE subcommand; the facilities described in this section are needed only if finer control is desired.
At any point in a MODULE, the current default optimization status, optimizeStatus, is given by:
$compileTimeValue("OPTIMIZE")
It may have one of four values at any given point in a compilation:
"OPTIMIZE" "NOOPTIMIZE"
"OPTIMIZEALL" "NOOPTIMIZEALL"
Each of these four values is also a $DIRECTIVE directive. An optimization directive has different effects depending on whether it is specified as a compiler subcommand, in a MODULE outside a PROCEDURE body, or within a PROCEDURE body. See Tables 17–1 and 17–2.
Table 17–1. Effects of Optimization Directives outside Any PROCEDURE Body or Specified as a Compiler Subcommand
| $DIRECTIVE Directive or Subcommand | Effect |
|---|---|
| OPTIMIZEALL | optimizeStatus := "OPTIMIZEALL" |
| NOOPTIMIZEALL | optimizeStatus := "NOOPTIMIZEALL" |
| OPTIMIZE | optimizeStatus := "OPTIMIZE" |
| NOOPTIMIZE | optimizeStatus := "NOOPTIMIZE" |
| OPTIMIZE p1 ... pn | add the pi to the list of procs to be optimized, and if necessary, remove them from the list of procs not to be optimized |
| NOOPTIMIZE p1 ... pn | add the pi to the list of procs not to be optimized, and if necessary, remove them from the list of procs to be optimized |
Table 17–2. Effects of Optimization Directives inside a PROCEDURE p
| $DIRECTIVE Directive | Effect |
|---|---|
| OPTIMIZEALL | error (not allowed inside a PROCEDURE) |
| NOOPTIMIZEALL | error (not allowed inside a PROCEDURE) |
| OPTIMIZE | same as OPTIMIZE p |
| NOOPTIMIZE | same as NOOPTIMIZE p |
| OPTIMIZE p1 ... pn | same as outside p |
| NOOPTIMIZE p1 ... pn | same as outside p |
At the start of each compilation optimizeStatus is set according to the compiler subcommands in effect (the initial default is NOOPTIMIZE).
Once OPTIMIZEALL or NOOPTIMIZEALL is in effect, any compiler directives that set optimizeStatus are ignored. This allows an OPTIMIZEALL or NOOPTIMIZEALL subcommand to override any directives in the source text.
After the compiler parses the entire MODULE, it does the following for each PROCEDURE for which a body is to be compiled into the MODULE:
An intmod records each PROCEDURE's marking. The default value of optimizeStatus used during an incremental recompilation of a PROCEDURE is the marked value of the PROCEDURE, if the PROCEDURE is marked; otherwise, the value of optimizeStatus as of the end of the original compilation is the default.
When code is generated for a PROCEDURE, it is not optimized if the PROCEDURE is debuggable. Otherwise, it is optimized or not, as marked; if unmarked, it is optimized if and only if the value of optimizeStatus at the end of the MODULE was OPTIMIZE.
At present, a PROCEDURE expanded inline is optimized if and only if the PROCEDURE in which it is expanded is optimized. This is subject to change.
The expression:
where p is a PROCEDURE name, yields the value given by:
The value of $compileTimeValue("OPTIMIZE p")
does not necessarily indicate whether
p really will be optimized
since the value can vary from one point in
the MODULE to another as the values it depends on vary.
MODLIB and INTLIB directory commands
display the O option if the value of
optimizeStatus at the end of the compilation
was OPTIMIZE or OPTIMIZEALL.
17.1.1. $compileTimeValue("OPTIMIZE")
As noted above, the current value of optimizeStatus is given by:
$compileTimeValue("OPTIMIZE")
$compileTimeValue("OPTIMIZE p")
IF optimizeStatus = "OPTIMIZEALL" OR
optimizeStatus NEQ "NOOPTIMIZEALL" AND
(p is on the list of procs to be optimized OR
(p is not on the list of procs not to be optimized
AND optimizeStatus = "OPTIMIZE"))
THEN "TRUE" EL ""
The conditions checked when checking is in effect are that:
The exceptions raised for these conditions are, respectively:
(see Chapter 20 for a discussion of exceptions).
Checking is controlled by the directives CHECK and NOCHECK, the compiletime pseudoprocedure CHECKING, and by several $DIRECTIVE directives.
In most cases, it is sufficient to specify checking at the per-MODULE level with the compiler's CHECK or NOCHECK subcommand; the facilities described in this section are needed only if finer control is desired.
Checking status is governed by two values, checkingStatus and localCheckingStatus, which interact to determine what code is checked and what is not. Checking is specifiable at the expression level. checkingStatus governs checking throughout a MODULE, whereas the directives affecting the value of localCheckingStatus apply only within a PROCEDURE.
Checking directives may surround a subscripted variable, preceding the ARRAY expression and following the closing square bracket, e.g.:
IF CHECK; a[i] NOCHECK; THENB ...
(in which case only a[i] is checked) or a field variable, preceding the POINTER or MODULE and following the last field, e.g.:
IF NOCHECK; p.f.g CHECK; THENB ...
(in which case only p.f.g is not checked). The effect of a checking directive placed within a subscripted or field variable (e.g., a NOCHECK; [i] CHECK;) is undefined. Checking directives may appear anywhere in the source between subscripted variables or field variables; it is often convenient to place them between statements or PROCEDUREs.
checkingStatus indicates the default for checking. It can have one of four values at any given point in a compilation:
"CHECK" "NOCHECK"
"CHECKALL" "NOCHECKALL"
Each of these four values is also a $DIRECTIVE directive. At the start of each compilation checkingStatus is set according to any compiler subcommands in effect (the initial default is CHECK).
localCheckingStatus indicates the current local (to a PROCEDURE) default for checking. It can have one of three values at any given point in a PROCEDURE:
"CHECK" "NOCHECK" ""
localCheckingStatus is set to the null STRING at the start of compilation and at the end of each PROCEDURE (so it is always "" outside of a PROCEDURE).
As a compilation proceeds, individual expressions as well as the PROCEDUREs containing them may be marked with a checking status. Each expression is marked when encountered with the current value of localCheckingStatus:
PROCEDUREs are marked if they contain CHECKALL or NOCHECKALL (the last value specified is the value used, if conflicting directives are specified), or if checkingStatus is CHECKALL or NOCHECKALL at the end of the MODULE, as described below. checkingStatus determines, at the end of the compilation, how code is generated for unmarked PROCEDUREs and expressions.
A checking directive has different effects depending on whether it is specified as a compiler subcommand, in a MODULE outside a PROCEDURE body, or within a PROCEDURE body. See Tables 17–3 and 17–4. The directives CHECK and NOCHECK are equivalent to $DIRECTIVE("CHECK") and $DIRECTIVE("NOCHECK"), respectively.
Table 17–3. Effects of Checking Directives outside Any PROCEDURE Body or Specified as a Compiler Subcommand
| $DIRECTIVE Directive or Subcommand | Effect |
|---|---|
| CHECKALL | checkingStatus := "CHECKALL" |
| NOCHECKALL | checkingStatus := "NOCHECKALL" |
| CHECK | checkingStatus := "CHECK" |
| NOCHECK | checkingStatus := "NOCHECK" |
| DEFAULTCHECK | no effect as directive; not allowed as subcommand |
| PUSHCHECK | no effect as directive; not allowed as subcommand |
| POPCHECK | no effect as directive; not allowed as subcommand |
Table 17–4. Effects of Checking Directives inside a PROCEDURE p
| $DIRECTIVE Directive | Effect |
|---|---|
| CHECKALL | mark this PROCEDURE to be checked |
| NOCHECKALL | mark this PROCEDURE not to be checked |
| CHECK | localCheckingStatus := "CHECK" |
| NOCHECK | localCheckingStatus := "NOCHECK" |
| DEFAULTCHECK | localCheckingStatus := "" |
| PUSHCHECK | push localCheckingStatus onto stack |
| POPCHECK | pop stack into localCheckingStatus |
There is a stack of localCheckingStatus values, the localCheckingStatus stack, that is cleared at the beginning of each PROCEDURE. It may be used within a PROCEDURE to preserve the localCheckingStatus; the recommended way to set or clear checking temporarily within a PROCEDURE body without affecting later code is:
$DIRECTIVE "PUSHCHECK","{NO}CHECK";
code to be affected by above CHECK or NOCHECK
$DIRECTIVE "POPCHECK";
It is an error to use POPCHECK if the localCheckingStatus stack is empty.
Once checkingStatus is set to CHECKALL or NOCHECKALL, any compiler directives that set checkingStatus are ignored. This allows a CHECKALL or NOCHECKALL subcommand to override any directives in the source text.
Before code is generated for the MODULE, the following occurs:
Code is generated for each PROCEDURE according to its marking, or, if the PROCEDURE is unmarked, code is generated for the individual expressions in accordance with their markings. If neither the expression nor its PROCEDURE is marked, then the value of checkingStatus determines whether checking is performed; checking is performed if checkingStatus is CHECK, not performed if it is NOCHECK.
The marking of PROCEDUREs and expressions is recorded in intmods, so that if a PROCEDURE is pulled in from an intmod, it is marked as it was when originally compiled. The final value of checkingStatus is recorded in an intmod and is used as the initial value of checkingStatus if the intmod is used for incremental recompilation.
INLINE PROCEDUREs are marked just like closed PROCEDUREs. When an INLINE PROCEDURE is expanded during code generation, the checking code generated in the INLINE PROCEDURE is independent of the checking status of the calling PROCEDURE.
localCheckingStatus is returned by:
The compiletime pseudoprocedure CHECKING returns the value given by:
if in a PROCEDURE, or:
outside of a PROCEDURE.
CHECKING does not necessarily indicate whether
checking is done at the point of call
since checkingStatus can change
before the end of the compilation, and only its final value affects the
generated code.
The MODLIB and INTLIB directory commands
show the letter C in the options column if the final value of
checkingStatus is CHECK or CHECKALL.
17.2.1. $compileTimeValue("CHECKINGSTATUS"),
$compileTimeValue("LOCALCHECKINGSTATUS"), and
CHECKING
checkingStatus is returned by:
$compileTimeValue("CHECKINGSTATUS")
$compileTimeValue("LOCALCHECKINGSTATUS")
checkingStatus = "CHECKALL" OR
checkingStatus NEQ "NOCHECKALL" AND
(current PROCEDURE is marked to be checked OR
current PROCEDURE is not marked not to be checked AND
(localCheckingStatus = "CHECK" OR
localCheckingStatus = "" AND checkingStatus = "CHECK"))
checkingStatus = "CHECKALL" OR checkingStatus = "CHECK"
17.3. Arithmetic Checking
Arithmetic checking enables the generation of extra code to detect
arithmetic overflow in (LONG) INTEGER and
(LONG) REAL operations.
On some processors, no extra code is required to detect certain kinds
of arithmetic overflow;
on such machines, an overflow exception may be generated whether or not
the ACHECK subcommand was in effect when the overflowing code was
compiled.
Furthermore, some kinds of overflow may be undetected on some
platforms even with ACHECK in effect.
Where extra instructions are required to detect overflow, arithmetically checked code may be significantly larger and slower.
Section 3.1 talks more about arithmetic overflow in MAINSAIL programs.
When arithmetic overflow is detected, the exception generated is usually $arithmeticExcpt; see Chapter 20.
Arithmetic checking facilities exactly parallel the standard runtime checking facilities, except that:
Specifically, the $DIRECTIVE directives for arithmetic checking are:
ACHECKALL NOACHECKALL
ACHECK NOACHECK
DEFAULTACHECK
PUSHACHECK POPACHECK
Arithmetic checking directives may apply to individual expressions. The effect is undefined unless the expression affected is surrounded by parentheses:
IF $DIRECTIVE "ACHECK"; (a .+ b) $DIRECTIVE "NOACHECK";
THENB ...
The MODLIB and INTLIB directory commands
show the letter A in the options column if the final value of
acheckingStatus is ACHECK or ACHECKALL.
17.4. Uninitialized Variable Checking
Uninitialized variable checking allows the compiler to issue an
error if an uninitialized variable is used; it is described in detail
in Section 4.44 of the MAINSAIL Compiler User's Guide.
Uninitialized variable checking is done on a per-MODULE basis, so the only available $DIRECTIVE directives related to it are:
UCHECK NOUCHECK
The last such directive encountered in a MODULE is the one that takes effect, if there are contradictory directives. By default, uninitialized variable checking is not done.
When the use of an uninitialized local variable is detected at runtime, one of two exceptions is raised (see Chapter 20 for a description of exceptions). The two exceptions are $uninitializedLocExcpt and $uninitializedProdExcpt.
The exception $uninitializedProdExcpt is generated when a PROCEDURE returns to its caller and one of its PRODUCES parameters is uninitialized. In all other cases where an uninitialized local variable is used, regardless of whether or not it is a PRODUCES parameter, the exception $uninitializedLocExcpt is generated.
Regardless which of the two above exceptions is raised, an exception
handler can obtain the name of the uninitialized local variable that
elicited the error by calling the system macro $exceptionStringArg1.
17.5. When Checking May Fail to Raise an Exception
MAINSAIL does not specify whether or not an exception is raised by an operation if the result of that operation is unused. For example, if ACHECK is in effect in the following code, where i, j, and k are all local variables:
$HANDLE i := j + k
$WITH ...
and the operation j + k overflows, an exception may or may not be raised if i is not used later in the same procedure. This is because the compiler may realize that the result of j + k is never used, and therefore decide not to generate any code for the operation.