4bsd-f77@utah-cs.UUCP (4.2 BSD f77 bug reports) (08/26/84)
From: Donn Seeley <donn@utah-cs.arpa> Subject: ASSIGNs to formal parameters don't work in f77 Index: usr.bin/f77 4.2BSD Description: If you ASSIGN a line number to a variable that is a formal parameter of a subroutine, your program will dump core when it executes a GOTO through that variable. This bug was found by Jerry Berkman and has been fixed by Bob Corbett and Jerry at various times; this fix draws on their work and some of my own. Repeat-By: Compile the following program (courtesy of Jerry Berkman). ---------------------------------------------------------------- call assok call assbad(n) end subroutine assok print *, ' in assok ' assign 200 to n go to n 100 print *, ' should never get here ' 200 return end subroutine assbad (n) assign 200 to n go to n 100 print *, ' should never get here ' 200 return end ---------------------------------------------------------------- When this program is run, it will print something like this: ---------------------------------------------------------------- in assok in assbad *** Segmentation violation Illegal instruction (core dumped) ---------------------------------------------------------------- Fix: There is a simple way to fix this, but I'm going to present a more complicated fix that does the job more elegantly. (Famous last words.) The immediate problem is that the routine putbranch() in putpcc.c has some incorrect special case VAX code to handle GOTOs through formal parameters to subroutines; the latter are identified by storage class STGARG. Here is the distributed version of putbranch(): ---------------------------------------------------------------- putbranch(p) register Addrp p; { #if TARGET == VAX if (p->vstg == STGARG) { putx(p); p2op(P2FORCE, P2LONG); putstmt(); p2pass("\tjmp\t*r0"); return; } #endif putex1(p); p2op(P2GOTO, P2INT); putstmt(); } ---------------------------------------------------------------- What this does is compute the operand and convert it to LONG; since a GOTO has no other expressions, it is guaranteed that the result of this will end up in register 0, and so putbranch() emits a VAX instruction intended to cause a indirect jump through register 0 (carefully wrapped in an intermediate code instruction which forces the VAX instruction to appear in the output literally). Unfortunately the addressing mode '*r0' is illegal and is not reported by the assembler, which instead generates a ridiculous operand; when this operand is evaluated the program crashes. The operand the coder probably wanted was '(r0)', which is reasonable and allows the program to work. It occurred to me to wonder why there is a special case for formal parameters at all, and of course what is going on here is that the first pass is covering up a bug in the code generator. The problem is that when presented an operand of GOTO of the form '*4(ap)' (a typical form for dereferencing a formal parameter, since arguments to subroutines in f77 are passed by reference), the code generator produces the instruction 'jmp **4(ap)', which is just as bogus as 'jmp *r0'. The assembler DOES detect this error, although its response is to dump core... You can see all this if you modify putbranch() in f77pass1/putpcc.c to remove the special case: ---------------------------------------------------------------- *** /tmp/,RCSt1024755 Sun Aug 19 22:31:02 1984 --- putpcc.c Sun Aug 19 20:12:03 1984 *************** *** 199,217 putbranch(p) register Addrp p; { ! #if TARGET == VAX ! if (p->vstg == STGARG) ! { ! putx(p); ! p2op(P2FORCE, P2LONG); ! putstmt(); ! p2pass("\tjmp\t*r0"); ! return; ! } ! #endif ! putex1(p); p2op(P2GOTO, P2INT); putstmt(); } --- 232,240 ----- putbranch(p) register Addrp p; { ! putex1((expptr) p); p2op(P2GOTO, P2INT); putstmt(); } ---------------------------------------------------------------- Now that I've tricked you into breaking your compiler by making the preceding change, I'll tell you how to fix it the right way. It requires a simple one-line change to the code table (f77/src/f1/table.c) to prevent GOTOs from having indirect operands: ---------------------------------------------------------------- *** /tmp/,RCSt1024493 Sun Aug 19 20:51:09 1984 --- table.c Sun Aug 19 20:50:52 1984 *************** *** 130,136 " jbr CL\n", GOTO, FOREFF, ! AWD, TANY, SANY, TANY, 0, RNOP, " jmp *AL\n", --- 144,150 ----- " jbr CL\n", GOTO, FOREFF, ! SNAME|SOREG, TANY, SANY, TANY, 0, RNOP, " jmp *AL\n", ---------------------------------------------------------------- Since no template will now match the GOTO in the example, the code generator is forced to write the operand out to register; when this is done the SAREG template matches and everything works out just the way the original coder wanted it to. GOTOs through local (SOREG) and COMMON (SNAME) variables use the changed template safely. Donn Seeley University of Utah CS Dept donn@utah-cs.arpa 40 46' 6"N 111 50' 34"W (801) 581-5668 decvax!utah-cs!donn