home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume43
/
pf77
/
part01
/
ifprob.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-06-10
|
30KB
|
928 lines
/*************************************************************************
i f p r o b i n s t r u m e n t a t i o n r o u t i n e s
************************************************************************
Copyright (c) 1990, Kevin Dowd.
You are free to use this software in any manner you choose. It is requested,
but not required, that you include the above copyright notice in any derived
works. The author make no claims as to the fitness or correctness of
this software for any use whatsoever, and it is provided as is. Any use
of this software is at the user's own risk.
This file contains routines that insert instrumenting code into
fortran source. They are called from routines found in ft.c.
Subroutine ifprob_body2 may be called recursively untill all fortran
constructs have been instrumented.
*/
#include <stdio.h>
#include "def.h"
#include "ext.h"
/* Data structures for ifprobbing:
The first is a stack for keeping track of the termination labels for
loops. A certain amount of transformation has to be done to correctly
instrument the termination point.
*/
int loopstack[MAXLSTACK];
int lstackptr = -1;
/* Specifically for ifprobbing:
*/
struct probnode *probstart = NULL; /* Profiling info is kept */
struct probnode *probptr, *probfree; /* in a linked list */
FILE *outifprobfp = stdout; /* fp for profiling output */
int ifprob = TRUE; /* Is ifprobbing turned on? */
char *root = {ROOT}; /* Variable root name */
int ifpcount = 0; /* Ifprobbing counter. */
/* Labels are given new values as encountered.
*/
struct lablnode *lstart, *lptr, *lfree;
int label_num = 10000; /* initial label number */
char ws[2000]; /* some general scratch space */
char *wsptr; /* a pointer into ws for incr_output */
int wsindent;
int executable_statement;
static int body_first_time;
static int preamble_dumped;
static int progtype;
/* These rules are first a handling the parameters that appear in
I/O statements.
*/
static RULE arglst = { "$B`,`6[,$X7]", ARGLST, NULL};
static RULE iolst = { "$LC4$X3", IOLST, NULL};
static RULE xeqx = { "$B`=`4!=$X5", XEQX, NULL};
/* This one is for computed gotos.
*/
static RULE lbllst = { "$N6[,$X7]", ARGLST, NULL};
/*************************************************************************
i f p r o b _ i n i t
************************************************************************/
int ifprob_init()
{
/* For ifprobbing: probnodes are allocated as needed. However, when
instrumenting more than one program module nodes already allocated
can be reused.
*/
struct probnode *getprobnode();
struct lablnode *getlablnode();
if (probstart == (struct probnode *) NULL)
probptr = probstart = getprobnode();
else {
probptr = probstart;
probfree = probstart->next;
probstart->next = (struct probnode *) NULL;
}
/* The same thing goes for new label nodes. These are used for generating
new label numbers in lieu of the ones in the source.
*/
if (lstart != (struct lablnode *) NULL) {
lfree = lstart;
lstart = (struct lablnode *) NULL;
}
/* Reset first time through flags so that stuff will be prepended to the
instrumented program body.
*/
body_first_time = TRUE;
preamble_dumped = FALSE;
/* Initialize the symbol table
*/
initsymtab();
/* Initialize list of implicit types.
*/
initimpllist();
}
/*************************************************************************
i f p r o b _ p r o g
************************************************************************/
int ifprob_prog (s, args)
int s;
struct arg *args[];
{
/* This routine is specific for processing the program statement
of a FORTRAN routine which is being 'ifprobbed'.
Inputs: s - an int telling what type of statement this is.
(i.e. IFTHEN, GOTO...)
args - argument pointers.
Modifies: ismain, modulename
Outputs: nothing
*/
/* If there was no program statement.
*/
if (s == NOMATCH) {
ismain = TRUE;
strcpy (modulename, "MAIN");
}
else {
switch (s) {
/* If there is a program statement, get it's name.
*/
case PROGRAM:
ismain = TRUE;
break;
/* Block data. Get the name.
*/
case BLOCKDATA:
ismain = FALSE;
break;
/* A function may be typed and may have an argument list. Record the module
name and add it to the symbol table. Do whatever may be necessary with
the input argument list.
*/
case FUNCTION:
ismain = FALSE;
break;
case SUBROUTINE:
ismain = FALSE;
break;
/* Record the module name. Output the input line.
*/
}
emitf77 (NULL,"%s", input_buffer);
strcpy (modulename, args[0]->text);
}
/* Record that we are inside a program module. Premature EOF will be an
error.
*/
inside_module = TRUE;
/* Record the program type so that we won't output any instrumention
preamble if this is a blockdata.
*/
progtype = s;
/* Record the symbol. Functions are typed. All others are typeless.
add: symbol,type,size,dim,use. This is the unofficial version
of the rules we are working with:
"PROGRAM$A0", PROGRAM
"BLOCKDATA$A0", BLOCKDATA
"[$LA1[*$LB2]]FUNCTION$A0[*$LB2][([$X3])]", FUNCTION
"[$LF1[*$N2]]FUNCTION$A0[*$N2][([$X3])]", FUNCTION
"SUBROUTINE$A0[($X3!)]", SUBROUTINE
*/
if (s == FUNCTION)
addsymtab (modulename, (args[1] != NULL ? args[1]->value : DEFAULT),
(args[2] != NULL ? atoi (args[2]->text) : DEFAULT),
SCALAR, DEFINITION, GLOBAL);
else
addsymtab (modulename, TYPELESS, UNDEFINED, UNDEFINED, DEFINITION,
GLOBAL);
/* Dissect the input argument list. This, unlike most other expressions
we'll encounter, cannot contain subexpressions.
*/
if (args[3] != (struct arg *) NULL)
dissectdmmy (args[3]->text);
#ifdef NEVER
fprintf (stderr,"program_statement: module=%s\n", modulename);
#endif
}
/*************************************************************************
i f p r o b _ d e c l
************************************************************************/
int ifprob_decl (label, s, args)
char *label;
int s;
struct arg *args[];
{
/* This routine is specific for processing the decls
of a FORTRAN routine which is being 'ifprobbed'.
Inputs: s - an int telling what type of statement this is.
args - argument pointers.
Modifies: nothing
Outputs: nothing
*/
/* If the your fortran compiler doesn't care where an implicit statement
occurs, then I don't either.
*/
switch (s) {
case IMPL:
dissectimpl (args[0]->value,
(args[1] == NULL ? DEFAULT : args[1]->value), args[2]->text);
break;
case EXTER:
case INTRINS:
/* Add externals to the symbol table. First module and then the rest.
*/
dissectnames (args[0]->text);
if (args[1] != (struct arg *) NULL)
dissectnames (args[1]->text);
break;
case COMPLX:
case INTEGER:
case REAL:
case LOGICAL:
case CHR:
/* The pass the type and possibly the length (if known).
*/
dissectdecl (s, args[0]->text,
(args[1] == NULL ? DEFAULT : args[1]->value));
break;
case DP:
dissectdecl (s, args[0]->text, 8);
break;
case DC:
dissectdecl (s, args[0]->text, 16);
break;
/* Dimension statements take previously specified or default type.
*/
case DIM:
dissectdecl (DEFAULT, args[0]->text, DEFAULT);
break;
/* Common statements can contain first references to variables and arrays.
*/
case COMMON:
case NAMELIST:
/* If this is a named common, make a reference to it.
*/
if (args[0] != (struct arg *) NULL)
addsymtab (args[0]->text, TYPELESS, UNDEFINED, UNDEFINED,
DEFINITION, GLOBAL);
/* Catalog the variables in the common.
*/
dissectdecl (DEFAULT, args[1]->text, DEFAULT);
break;
default:
if (preamble_dumped == FALSE)
ifprob_preamble();
preamble_dumped = TRUE;
}
emitf77 (label,"%s", input_buffer);
}
/*************************************************************************
i f p r o b _ b o d y
************************************************************************/
int ifprob_body (label, s, args, ibufptr)
char *label;
int s;
struct arg *args[];
char *ibufptr;
{
/* This is a header routine for the one that actually instruments the code.
For reasons surrounding instrumentation of do-loop terminals, it is
often better to delay the instrumented output. The routines incr_output
and emitf77 are for delayed and immediate output, respectively.
*/
int q;
struct probnode *getprobnode();
if (body_first_time == TRUE) {
/* At the top of the program body a branch to the end is emitted where
one of the statistics keeping routines is told how much space to set
aside for gathering statistics for this routine.
*/
if (preamble_dumped == FALSE)
ifprob_preamble();
preamble_dumped = TRUE;
/* Check for and skip past statement functions.
*/
if (statement_fn (s, args) == TRUE) {
emitf77 (NULL, "%s", ibufptr);
return (OK);
}
ifprob_branch ();
/* Turn implicit declarations explicit.
*/
explicit();
/* Count the number of times this routine has been entered
*/
emitf77 (NULL, "%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
probptr->linenumber = source_line_number-1;
probptr->type = ENTRY;
probptr = probptr->next = getprobnode();
ifpcount+=1;
body_first_time = FALSE;
}
reset_incr_output();
executable_statement = TRUE;
q = ifprob_body2 (s, args, ibufptr);
if (q != NOMATCH) emitf77 (label, NULL, NULL);
return(q);
}
/*************************************************************************
i f p r o b _ b o d y 2
************************************************************************/
int ifprob_body2 (s, args, ibufptr)
int s;
struct arg *args[];
char *ibufptr;
{
/* This routine is specific for processing the body of a FORTRAN routine
which is being 'ifprobbed'.
Inputs: label - statement label if there is one, or NULL
s - an int telling what type of statement this is.
(i.e. IFTHEN, GOTO...)
args - argument pointers.
Modifies: ifpcount, makes probnodes.
Outputs: nothing
*/
struct probnode *getprobnode();
int newlabel();
char *ltext();
char chtemp[6];
int unitfound, formatfound; /* flags for handling I/O statements. */
int stype;
char *car, *cddr;
struct arg *part2, *part1;
struct arg *args2[MAXARGS];
#ifdef DEBUG
printf ("ifprob_body2: statement type = %d\n",s);
#endif
switch (s) {
case IFTHEN:
/* For a logical IF followed by a THEN: Instrument the lines just before
and just after to measure how often the condition is true.
*/
incr_output (NL, "IF(%s)THEN", args[0]->text);
incr_output (NL, "%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
probptr->linenumber = source_line_number;
probptr->type = IFTHEN;
probptr = probptr->next = getprobnode();
ifpcount+=1;
break;
case ELSIFTH:
/* In the middle of a block if: Measure the number of times this condition
is true.
*/
incr_output (NL, "ELSEIF(%s)THEN", args[0]->text);
incr_output (NL, "%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
probptr->linenumber = source_line_number;
probptr->type = ELSIFTH;
probptr = probptr->next = getprobnode();
ifpcount++;
break;
case ELS:
/* It should be clear how many times we make it to the else clause
if we were to calculate it.
*/
incr_output (NL, "ELSE");
incr_output (NL, "%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
probptr->linenumber = source_line_number;
probptr->type = ELS;
probptr = probptr->next = getprobnode();
ifpcount++;
break;
case ELSIF:
/* Turn an else-if into an else-if-then and add a line to measure how
often it is true.
*/
incr_output (NL, "ELSEIF(%s)THEN", args[0]->text);
incr_output (NL, "%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
ifpcount++;
/* Recursively match and parse the logical consequent.
*/
recursive_body (args[1]->text, args2, ifprob_body2);
probptr->linenumber = source_line_number;
probptr->type = ELSIF;
probptr = probptr->next = getprobnode();
break;
case LOGIF:
/* This LOGICAL IF will be turned into a BLOCK IF with code inserted
to measure activity. There is a danger here; A one line statement
is being turned into a three line statement. If the statement turns
out to be a do loop terminal it is important to execute every bit
of the transformed version. Also, the consequent may need to be
parsed as well. For these two reasons, output is delayed until
after the whole statement is parsed. The consequent is parsed
recursively within the context of the current statement.
By taking these careful steps such constructs as
DO 10 I=1,N
5 CALL GZERNINFRATZ (A,B,C)
10 IF (A(I) .EQ. U) IF (B(I) .EQ. V) IF(C(I) .EQ. W) GOTO 5
Will be parsed correctly (even if they're illegal).
*/
incr_output (NL,"IF(%s)THEN", args[0]->text);
incr_output (NL,"%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
ifpcount+=1;
/* Recursively match and parse the logical consequent.
*/
recursive_body (args[1]->text, args2, ifprob_body2);
incr_output(NL,"ENDIF");
probptr->linenumber = source_line_number;
probptr->type = LOGIF;
probptr = probptr->next = getprobnode();
incr_output (NL,"%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
probptr->linenumber = source_line_number;
probptr->type = LOGIF;
probptr = probptr->next = getprobnode();
ifpcount+=1;
break;
case ENDIF:
/* Anything could've happened within the block if. Instrument the outside.
*/
incr_output (NL, "ENDIF");
incr_output (NL, "%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
probptr->linenumber = source_line_number;
probptr->type = ENDIF;
probptr = probptr->next = getprobnode();
ifpcount++;
break;
case ARITHIF:
/* An arithmetic IF will be replaced with an IF-THEN-ELSEIF-THEN-ENDIF
sequence.
*/
incr_output (NL,"%sRR=%s", root, args[0]->text);
incr_output (NL,"IF(%sRR.LT.0)THEN",root);
incr_output (NL,"%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
incr_output (NL,
"GOTO%s",(member_lstack(newlabel(args[1]->text)) == TRUE ?
ltext(newlabel(args[1]->text)+2) :
ltext(newlabel(args[1]->text))));
probptr->linenumber = source_line_number;
probptr->type = ARITHIF;
probptr = probptr->next = getprobnode();
incr_output (NL, "ELSEIF(%sRR.EQ.0)THEN", root, args[0]->text);
incr_output (NL, "%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount+1,root,root,ifpcount+1);
incr_output (NL,
"GOTO%s",(member_lstack(newlabel(args[2]->text)) == TRUE ?
ltext(newlabel(args[2]->text)+2) :
ltext(newlabel(args[2]->text))));
incr_output (NL, "ELSE");
incr_output (NL,
"GOTO%s",(member_lstack(newlabel(args[3]->text)) == TRUE ?
ltext(newlabel(args[3]->text)+2) :
ltext(newlabel(args[3]->text))));
incr_output (NL, "ENDIF");
probptr->linenumber = source_line_number;
probptr->type = ARITHIF;
probptr = probptr->next = getprobnode();
ifpcount+=2;
break;
case GOTO:
/* GOTOs are important because the label following must be converted
to one of ours.
*/
incr_output (NL,
"GOTO%s",(member_lstack(newlabel(args[0]->text)) == TRUE ?
ltext(newlabel(args[0]->text)+2) :
ltext(newlabel(args[0]->text))));
break;
case CGOTO:
/* The spot immediately following the goto needs to be instrumented. Then
all of the label numbers have to be changed.
*/
part2 = args[0];
incr_output (NL, "GOTO(",args[0]->text);
incr_output (OL, "%s",
(member_lstack(newlabel(args[1]->text)) == TRUE ?
ltext(newlabel(args[1]->text)+2) :
ltext(newlabel(args[1]->text))));
cddr = args[2]->text;
while (match (cddr, &lbllst, args) != NOMATCH) {
cddr = (args[7] == NULL ? "" : args[7]->text);
car = args[6]->text;
incr_output (OL, ",%s",
(member_lstack(newlabel(car)) == TRUE ?
ltext(newlabel(car)+2) :
ltext(newlabel(car))));
}
incr_output (OL, "),%s", part2->text);
incr_output (NL, "%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
probptr->linenumber = source_line_number;
probptr->type = CGOTO;
probptr = probptr->next = getprobnode();
ifpcount++;
break;
case IOSTMT:
/* I/O statements can contain labels for transfer of flow upon reaching an
error. These have to be changed to our internal numbers. It is not my
place to anticipate every type of I/O specifier ever written, since this
is an area where each manufacturer stretches their legs. Additionally,
non-positional, label driven parameters are a pain to match. The innards
of these I/O statements will be disected in search of 'END=' and 'ERR='.
$LD3($X0!)[$A1[,$X2]]
\
\ match
\
(*) $B`,`$6[,$X7] --- else done
\
\ match
\ else
$LC4$X3 --- match --- $B`=`4!=$X5 ---> output
{end= \ \
err= \ newlabel \ else
fmt=} \ if unit=NULL go to (*)
go back to (*) unit=$6
elseif fmt=NULL
fmt =$6 ---> output
*/
/* Initialize some local vars needed to keep track of parsing the IO
statement.
*/
unitfound = FALSE;
formatfound = FALSE;
stype = args[3]->value;
cddr = args[0]->text;
part1 = args[1];
part2 = args[2];
/* Output the first part of the statement
*/
incr_output (NL,"%s(",args[3]->text);
/* The argument list is iteratively disassembled by the rule "$B`,`6[,$X7]".
*/
while (match (cddr, &arglst, args) != NOMATCH) {
cddr = (args[7] == NULL ? "" : args[7]->text);
car = args[6]->text;
/* Check for the strings err=, end= and fmt=. These have special meaning to
us since we mean to replace the labels. The new format label number is
bumped up by one. This is so that if a branch was being taken with the
format label as the target it can be instrumented.
*/
if (match (car, &iolst, args) != NOMATCH) {
switch (args[4]->value) {
case ERREQ:
incr_output(OL,
"ERR=%s%s",ltext(newlabel(args[3]->text)),
(*cddr == '\0' ? "" : ","));
break;
case ENDEQ:
incr_output(OL,
"END=%s%s",ltext(newlabel(args[3]->text)),
(*cddr == '\0' ? "" : ","));
break;
case FMTEQ:
formatfound = TRUE;
incr_output(OL,"FMT=%s%s",
(isdigit(*(args[3]->text)==TRUE)
? ltext(newlabel(args[3]->text)+1)
: args[3]->text),
(*cddr == '\0' ? "" : ","));
}
}
/* Otherwise, check for anything that looks like 'foo=bar'. If this
is not an I/O specifier of that type, then it might be the unit
or format specifier.
*/
else if (match (car, &xeqx, args) != NOMATCH)
incr_output (OL,"%s%s",car,(*cddr == '\0' ? "" : ","));
else {
if (unitfound == FALSE) {
incr_output(OL,"UNIT=%s%s",car,
(*cddr == '\0' ? "" : ","));
unitfound = TRUE;
}
/* It can only be a format specifier if this is a read or write statement.
A lone label in any other type of I/O statement is probably an error,
but I will let it go.
*/
else if (formatfound == FALSE &&
(stype == WRT || stype == RD)) {
incr_output(OL,"FMT=%s%s",
(isdigit(*car) == TRUE
? ltext(newlabel(car)+1)
: car), (*cddr == '\0' ? "" : ","));
formatfound = TRUE;
}
else
incr_output(OL,"%s%s",car,(*cddr == '\0' ? "" : ","));
}
}
incr_output (OL,")");
if (part1 != NULL) {
incr_output (OL,"%s",part1->text);
if (part2 != NULL)
incr_output (OL,",%s",part2->text);
}
incr_output (NL, "%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
probptr->linenumber = source_line_number;
probptr->type = IOSTMT;
probptr = probptr->next = getprobnode();
ifpcount++;
break;
case PRTNRD:
/* Print and Read statements without cilists have format specifiers that
have to be looked at.
*/
part1 = args[1];
part2 = args[2];
if (isdigit(*(args[0]->text)))
incr_output (NL, "%s%s",args[3]->text,
ltext(newlabel(args[0]->text)+1));
else
incr_output (NL, "%s%s",args[3]->text, args[0]->text);
if (part1 != NULL) {
incr_output (OL,"%s",part1->text);
if (part2 != NULL)
incr_output (OL,",%s",part2->text);
}
break;
case FORMAT:
/* All labels are potential branch targets. Labels on format statments
are different and must be treated differently. They statement is marked
non-executable so it will receive special treatment by emitf77.
*/
executable_statement = FALSE;
incr_output (NL,"FORMAT(%s)",args[0]->text);
break;
case DOLOOP:
/* Instrument a vanilla do-loop structure. The interesting count is the
number of times through the loop. In theory the number of times the
loop has been entered is already recorded. There is a problem at the
bottom of the loop. Assume:
DO 10 I=1,10
IF (Z) GO TO 10
10 J = J * 1 + 8 / K.... <- exact number of counts here difficult.
IF (X) GO TO 10
A working transformation would be:
DO 11 I=1,10
IF (Z) GO TO 12
12 J = J * 1 + 8 / K .... <- count
11 CONTINUE
GO TO 13
10 J = J * 1 + 8 / K .... <- count
13 CONTINUE
IF (X) GO TO 10
with the two counts added to get a correct measure of the number of
times the line computing J is executed.
The algorithm will be:
Look up new label
If this is the start of a loop,
add label to lstack (stack of loop labels)
add 1 to number returned.
Else if this a termination point for a loop we are currently in
add 2 to the number returned.
Upon reaching the termination point, add the nightmare shown
just above.
Update: added support for Do ... enddo. November 23, 1990.
Update: added support for Dowhile ... enddo, March 26, 1993.
*/
/* Add the new termination point to the labelstack and (possibly) to
the list of known labels.
*/
if (args[0] != (struct arg *) NULL) {
push_lstack (newlabel(args[0]->text));
strcpy (chtemp ,ltext(newlabel(args[0]->text)+1));
incr_output (NL,
"DO%s%s=%s,%s,%s",chtemp,
args[1]->text, args[2]->text, args[3]->text,
(args[4] == NULL ? "1" : args[4]->text));
}
/* No label to worry about with do...enddo
*/
else {
incr_output (NL, "DO%s=%s,%s,%s",
args[1]->text, args[2]->text, args[3]->text,
(args[4] == NULL ? "1" : args[4]->text));
}
incr_output (NL,
"%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
probptr->linenumber = source_line_number;
probptr->type = DOLOOP;
probptr = probptr->next = getprobnode();
ifpcount+=1;
break;
case DOWHILE:
incr_output (NL, "DOWHILE(%s)", args[0]->text);
incr_output (NL, "%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
probptr->linenumber = source_line_number;
probptr->type = DOWHILE;
probptr = probptr->next = getprobnode();
ifpcount+=1;
break;
/* Do something with assignment statements.
*/
case ASSIGN:
if (args[1] != NULL)
incr_output (NL, "%s(%s)=%s",args[0]->text, args[1]->text,
args[2]->text);
else
incr_output (NL, "%s=%s",args[0]->text, args[2]->text);
break;
/* We need to be sure that the exit routine is called on the way out. All
stop statements are modified.
*/
case STOP:
incr_output (NL,"CALL%sXX",root);
if (args[0] != (struct arg *) NULL)
incr_output (NL,"STOP%s", args[0]->text);
else
incr_output (NL,"STOP");
break;
/* Do...endo loop termination needs a count to be taken after the loop
terminal
*/
case ENDDO:
incr_output (NL, "ENDDO");
incr_output (NL, "%sSS(%sOO+%d)=%sSS(%sOO+%d)+1",
root,root,ifpcount,root,root,ifpcount);
probptr->linenumber = source_line_number;
probptr->type = ENDDO;
probptr = probptr->next = getprobnode();
ifpcount++;
break;
case RETN:
if (args[0] != NULL)
incr_output (NL, "RETURN%s",args[0]->text);
else
incr_output (NL, "RETURN");
break;
/* This is a kludge until all of the possible source rules are implemented
in rules.h. Upon hitting the end, return NOMATCH to get out of the
program body loop. The 'args[0]' check is to unsure against other END
sorts of things... like 'END DO'.
*/
case ENDSTMT:
if (args[0] == (struct arg *) NULL)
return (NOMATCH);
/* Look at the label if there is one. The label number produced in the
output may have to be adjusted according to the scheme for instrumenting
a doloop as discussed above.
*/
default:
stmt_buffer_empty = TRUE;
incr_output (NL,"%s", ibufptr);
}
}
/*************************************************************************
i f p r o b _ e n d
************************************************************************/
ifprob_end()
{
struct lablnode *local, *last;
inside_module = FALSE;
stmt_buffer_empty = TRUE;
/* Do other stuff:
*/
ifprob_postamble ();
emitf77 (NULL, "END");
if (ismain == TRUE)
ifprob_routines ();
/* Chain the remaining free label nodes onto the current list so that they'll
be reused.
*/
if (lstart != (struct lablnode *) NULL) {
local = lstart;
while (local != (struct lablnode *) NULL) {
last = local;
local = last->next;
}
last->next = lfree;
}
/* Create history file entry for the ifprob data to go into when the program
is run.
*/
ifprob_history ();
ifpcount = 0;
/* dump_symtab();
*/
}
/*************************************************************************
i f p r o b _ c l n p
************************************************************************/
ifprob_clnp()
/* Perform final cleanup before leaving the ifprobber.
*/
{
if (inside_module == TRUE)
ifprob_end();
}