[net.bugs.4bsd] [4bsd-f77 #33] ASSIGNs to formal parameters don't work in f77

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