MAINSAIL Language Manual, Chapter 17

previous   next   top   contents   index   framed top   this page unframed


17. Optimization and Checking

Optimization and checking govern the quality of object code produced and the amount of checking of various runtime error conditions. Facilities are provided to optimize or check whole MODULEs or individual PROCEDUREs, and to check parts of PROCEDUREs. The facilities for arithmetic checking parallel the standard checking facilities.

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.

17.1.1. $compileTimeValue("OPTIMIZE")

As noted above, the current value of optimizeStatus is given by:

$compileTimeValue("OPTIMIZE")

The expression:

$compileTimeValue("OPTIMIZE p")

where p is a PROCEDURE name, yields the value given by:

IF  optimizeStatus = "OPTIMIZEALLOR
    
optimizeStatus NEQ "NOOPTIMIZEALLAND
        (
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 "TRUEEL ""

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.2. Checking

The standard checking directives cause the compiler to emit code to issue error messages for certain runtime conditions that cannot be determined at compiletime. This causes more code to be generated, and thus results in slower execution. Sections of code on which such checking is performed are said to have checking in effect. It can be very difficult to track bugs in code where checking is not in effect; error messages are suppressed, and the error condition has undefined effects.

The conditions checked when checking is in effect are that:

  1. ARRAY subscripts are within bounds (except for the upper bound of the first dimension of an unsized inplace ARRAY; see Section 12.14).

  2. A dynamic ARRAY used in a subscripted variable is not Zero.

  3. A POINTER used as the base part of a field variable is not Zero.

  4. Linkage has been established to a MODULE of which an interface variable is indirectly accessed (see Section 11.9).

The exceptions raised for these conditions are, respectively:

  1. $subscriptExcpt

  2. $nullArrayExcpt

  3. $nullPointerExcpt (for data access) or $nullCallExcpt (for PROCEDURE access)

  4. $unboundModuleExcpt

(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 CHECKa[iNOCHECKTHENB ...

(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 NOCHECKp.f.g CHECKTHENB ...

(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.

17.2.1. $compileTimeValue("CHECKINGSTATUS"), $compileTimeValue("LOCALCHECKINGSTATUS"), and CHECKING

checkingStatus is returned by:

$compileTimeValue("CHECKINGSTATUS")

localCheckingStatus is returned by:

$compileTimeValue("LOCALCHECKINGSTATUS")

The compiletime pseudoprocedure CHECKING returns the value given by:

checkingStatus = "CHECKALLOR
checkingStatus NEQ "NOCHECKALLAND
    (
current PROCEDURE is marked to be checked OR
      
current PROCEDURE is not marked not to be checked AND
     (
localCheckingStatus = "CHECKOR
      
localCheckingStatus = "" AND checkingStatus = "CHECK"))

if in a PROCEDURE, or:

checkingStatus = "CHECKALLOR checkingStatus = "CHECK"

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.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.


previous   next   top   contents   index   framed top   this page unframed

MAINSAIL Language Manual, Chapter 17