There are two types of fault which can be detected by the compiler, those which can be found during compiling and those which become evident during the running of the compiled program. To aid the programmer in correcting these faults information is automatically printed out where a fault occurs.
During compiling an outline of the program is produced as an aid to the finding of faulty instructions. It also associates each block and routine with its serial number, for use in tracing faults found at run time (see later). All faults during compiling are monitored. Those to which a line number can be attached, such as NAME NOT SET, are preceded by it, while those which can only be found at the end of a routine such as TOO FEW REPEATS are monitored after the END. In calculating the line number, blank lines are ignored, and lines joined by the continuation symbol c count as one. Finally at the end of each routine all the non-local variables except the permanent routines and functions are printed out. Although these do not necessarily indicate a fault, they may indicate a name which should have been declared locally. A typical program monitor might be
0 BEGIN BLOCK : SERIAL NO = 89, M/C ADDRESS = 2721 26* NAME TEMP NOT SET 55* LABEL 7 SET TWICE 70 BEGIN ROUTINE POLY:SERIAL NO = 90, M/C ADDRESS = 3210 115* NAME TEMP NOT SET 115* REAL NAME X IN EXPRESSION 120 END ROUTINE POLY : OCCUPIES 256 M/C INSTRUCTIONS * LABEL 18 NOT SET NON-LOCAL VARIABLES A TEMP1 S1 182 END OF PROGRAM: OCCUPIES 800 M/C INSTRUCTIONS
The above should be self-explanatory. It indicates that the program started at line 0 and finished on line 182. These are physical lines and exclude all blank lines on the print-out. The outer block is given the serial number 89. The routine POLY started on line 70 and was given the serial number 90. There were mistakes in lines 26 and 55 and two in line 115. Finally label 18 was not set in the routine POLY.
Since there may be more than one statement on a line, it is not possible to tell specifically which statement is involved but the faults are printed in the order in which they are discovered. A full list of faults is given in Appendix 4 together with a brief description of their nature.
During the running of a program certain faults may be detected both by the compiler and by the machine and its supervisor program. For example, the supervisor program detects the case where the square root of a negative argument is being requested and the compiler detects faults connected with switch and test instructions.
The standard procedure is to print out 2 lines of information specifying the fault and the line on which it occurs followed by a list of useful information found in the FIXED part of the stack. For example:-
LINE 117 ROUTINE 90 EXP OVERFLOW ROUTINE 90 ARRAY(1:10,1:5) ARRAY(1:10,1:5) 10 5 0.3333333341α 0 -1.1249999997α -1 0.0000000000α-99 6 4 CYCLE(CURRENT VALUE = 6, FINAL VALUE = 10, INCREMENT = 1) CYCLE(CURRENT VALUE = 4, FINAL VALUE = 5, INCREMENT = 1) BLOCK 89 0.0000000000α-99 3.7152463802α 3 10 5 3 6 ARRAY(1:10,1:5) ARRAY(1:10,1:5)
indicates that an instruction in line 117 routine 90 (the line number refers to the entire program, not just the routine), resulted in exponent overflow. Then follows a list of the scalars, array dimensions and cycles of the routine in the order in which they were originally declared, followed by the list for the routine or block which called this routine, then that of the routine which called it and so on until the main block is reached. Thus the above might correspond to:-
begin real a,b integer i,j,k,l array X,Y(1:10,1:5) --- --- matrix fn (X,Y,i,j) --- --- routine matrix fn(arrayname A,B integer m,n) real a,b,c ; integer i,j --- --- cycle i = 1,1,m cycle j = 1,1,n --- --- repeat repeat end end of program
The above standard monitoring procedure involving the termination of the program, is not always convenient. For example if a program is dealing with a series of data sets, it may be preferable to restart on the next 'case' in the event of (say) EXP OVERFLOW rather than terminate the entire job.
An instruction is provided which enables the user to trap certain faults and transfer control to some preassigned point in the program. It takes the form:-
fault [FAULT LIST] where [FAULT LIST] = [N-LIST]->[N] [REST OF FAULT LIST]
For example:-
fault 1,2,5 ->18, 3,4 ->10
means 'if a fault of type 1,2 or 5 subsequently occurs then jump to label 18 ; and if a fault of type 3 or 4 occurs then jump to label 10. The effect is to preserve all the necessary control data to enable control to revert to this point in the program (and then jump to label 18 or 10) should one of the specified types of faults occur at some lower (or the same) level. The label must be in the same block as the trapping statement, which will usually be in the 'main' block at (say) level 1 or 2.
The fault instruction has dynamic significance, and a following fault instruction can change this trapping action. All faults not referred to by a fault instruction are dealt with in the usual way (i.e. they cause the program to be terminated).
The first two lines of the standard fault monitoring are printed for faults trapped in this way. Appendix 4 contains the list of faults which can be trapped and the corresponding fault numbers.
Provision is made to compile certain checking facilities in selected parts of the program. Having been compiled they can then be switched on or off at run time by means of instructions in the program. The formats are:-
compile[check] stop[check] [check]on [check]off
The first pair of statements are DECLARATIVES which delimit the areas of the program in which provision is to be made for the particular checking facility. The second pair are INSTRUCTIONS which turn the facility on or off (initially they are on). They do this by setting a certain switch which is examined whenever the facility is about to be executed. If the relevant switch is on then the facility is executed, if off it is skipped. If the facility has not been compiled in the first place then the instructions have no effect. This switch setting is extremely fast so that there is nothing to be gained from recording the current state of the switch (in some integer, say), and testing this before each setting order. For example, the following sequence causes queries to be printed every tenth time round the cycle.
cycle i = 1,1,m queries off if fracpt(1/10) = 0 then queries on --- --- repeat
The switch sensing on the other hand is a time consuming operation and it is for this reason that the declaratives are provided to delimit the areas of the program in which this takes place. In most cases, however, the check is compiled over the entire program.
The checking facilities in question are described by the phrase:-
[check] = queries, routine trace, jump trace, array bound check
They will be described in turn.
Any arithmetic instruction (including complex) can be followed by a ?, for example:-
a = b(i) + c?
When the facility is operative the new value on the l.h.s. is printed every time the instruction is obeyed. The style of printing will be fixed, floating, or complex floating according as the l.h.s. is of integer, real, or complex type.
[Unlike the other facilities, ?'s are normally compiled so that a compile queries at the head of a program is redundant. Also ignore queries is equivalent to stop queries.]
When the routine trace is operative it causes the routine number to be printed each time a routine or block is entered and left. The correspondence between the routine number and the name can be found from the program outline produced during compilation. The printout of a routine trace might appear
R95 R97 RF96 END96 END97 R99.......
Here R, RF denote routine and real fn respectively. The full list of abbreviations is: -
B begin R routine IF integer fn RF real fn CF complex fn IM integer map RM real map CM complex map
The jump trace facility allows the flow of the program to be followed in greater detail. For every jump instruction obeyed the label number is printed; for every test the value of the label at which the [COND] is satisfied is printed; for every switch the value of the switching index is printed. Thus a label trace might appear
->3 T1 ->4 ->6 S3 ->7 ->8 ->9
Here T and S refer to test and switch respectively. If the label and routine trace are both operative the print out might appear:-
R95 ->3 T1 ->4 ->5 R97 S3........
If this facility is operative the values of the subscript expressions in all array elements are checked to see if they lie in the range specified by the bound pairs in the array declarations. If not, the program is terminated with the appropriate monitoring.
Certain checks are built into the object program e.g., whether a cycle instruction calls for an integral number of cycles and whether a switch index is out of range or corresponds to a label not set. All are time and space consuming operations. They can be removed from an object program which is otherwise ready for production by means of the declaration
production run