[mod.computers.vax] Null arguments in VMS Fortran

Schauble@MIT-MULTICS.ARPA (Paul Schauble) (01/02/87)

I have some fortran programs taken from a VAX to convert.  These make
use of null arguments in CALL statements, for example,
    CALL SUBR (A,,B)

I understand that this produces a word of zeros in the generated
argument list.  What I don't understand is what this feature is used
for.

As I recall, page 0 of the process address space does not exist, so that
references to small addresses produce some variety of memory protection
fault.  This would seem to be the fate of a fortran subroutine that
attempts to use a null argument.  Is there a language extension that
would let it tell?

Am I correct then in assuming that this feature requires that the
routine being called must be written in another language and must be
expecting use of this convention?

          Paul
         <Schauble at MIT-Multics.arpa>

stew%lhasa@HUCSC.HARVARD.EDU.UUCP (01/02/87)

The subroutine with the null argument can check for that by
comparing %ADDR(X) to zero.  Generally, this form is only used
in calling system services which take optional arguments.
Needless to say, the %ADDR feature makes you VMS dependant.

Stew

Schauble@MIT-MULTICS.ARPA.UUCP (01/05/87)

My previous question about null arguments produced two replies to the
effect that, yes, the subroutine can be written in Fortran. They
proposed something like

        CALL SUBB(X,,Y)

        subroutine subb(a,b,c)
        ...
        v = default
        if (%loc(b).ne.0) v = b

Assume that this is the only reference to b in the subroutine.

This looks good except for one small problem. Several compilers that I
have dealt with that use a similar linkage convention will generate a
preamble that will move read-only formal parameters into the
subroutine's local data space. Further references can be done faster
than the indirect reference usually needed for a parameter.

I know that VMS Fortran has a very good optimizer. Does it really not do
this optimization? And does the above really work?

          Thanks,
          Paul <Schauble at MIT-Multics>

LEICHTER-JERRY@YALE.ARPA.UUCP (01/05/87)

    My previous question about null arguments produced two replies to the
    effect that, yes, the subroutine can be written in Fortran. They
    proposed something like
    
            CALL SUBB(X,,Y)
    
            subroutine subb(a,b,c)
            ...
            v = default
            if (%loc(b).ne.0) v = b
    
    Assume that this is the only reference to b in the subroutine.
    
    This looks good except for one small problem. Several compilers that I
    have dealt with that use a similar linkage convention will generate a
    preamble that will move read-only formal parameters into the
    subroutine's local data space. Further references can be done faster
    than the indirect reference usually needed for a parameter.
    
    I know that VMS Fortran has a very good optimizer. Does it really not do
    this optimization? And does the above really work?
The VAX has a flat address space, so the notion of "the subroutine's local
data space" makes little sense - stuff is equally accessible whereever in
memory it happens to be.  The VAX addressing modes allow for a double
indirection (if you are not trying to do much else with the address at the
same time), so you would save a single memory access, rather than a whole
instruction - not often worth it.

What DOES make sense - and IS done by the compiler - is placing heavily-used
objects in registers.  If b is used, and will itself fit in a register, it may
very well get loaded into one.  If b is a large object - for example, and
array - it can't be loaded into a register, but its address (i.e., the address
of b(0)) can be.

Usually, there is no advantage to putting a quantity in a register before it
is used.  In fact, that just ties up a register that could be used for some-
thing else.  Also, %loc(b) is a reference to b's address, not to b, so cannot
use a register copy of b anyway.  (It COULD use a register copy of b's address,
but it is safe to load that.)

In the example you give, it is highly unlikely that any optimizer would choose
to put b in a register (or even copy it) - with only a single reference to b
itself, it costs more to load up the register than can be saved by having b
available there.  So you are PROBABLY safe.  However, without specific
indications in the documentation indicating that this is, indeed, supported,
be aware that you are taking SOME risk; it just could break in a later version
of the compiler.

Personally, in most situations, if I really needed this feature I'd make sure
the current compiler accepted it, then go ahead and use it - recognizing that
I MIGHT get burned.  (I use optional arguments in C code all the time, but
because C uses call by value rather than call by reference, the problems
discussed here can't arise.)

I don't think I've ever used embedded optional arguments, though - just
optional TRAILING arguments.  Functions decide whether they are there by
checking how many arguments they were called with - a 3-line or so MACRO
program returns that information.  (There is a related problem here with
functions ALL of whose arguments are optional - if you don't provide at least
one formal, or if you provide one but then never use it, the C compiler will
go ahead and use the AP as a work register.  Since you need to AP to find the
arguments, this will cause problems!  So its important to provide a formal and
keep it "live" until you have a chance to pick up and save AP - another tiny
MACRO program.  I do this by writing such functions with a single formal, then
calling the MACRO program with the formal as an argument.  The MACRO program
never looks at its arguments, but this keeps the optimizer at bay.)

							-- Jerry

For the curious:

------------------------Extract from SUPPORT.MAR------------------------------
	.TITLE		SUPPORT - Simple support routines
	.IDENT		'V01-000'
; EDITLEVEL=27

	.PSECT		SUPPORT_CODE,CON,EXE,GBL,PIC,SHR,RD,REL,NOWRT

	.SUBTITLE	Get number of arguments
;
;	int
;	nargs()
;
; Return to caller the number of arguments he was called with.  Only correct
; when all the arguments are actually longwords (e.g., not C double's, or
; struct's by value).

.ENTRY	nargs,^M<>
	MOVZBL		@8(FP),R0		;R0 <- count
	RET					;Easy enough


	.SUBTITLE	Get argument list
;
;	ARGLIST *
;	arglist()
;
; Return a pointer to the caller's argument list.
;
; Note that the VAX C compiler will use AP as a working register in a function
; that has no arguments.  (It could presumably also do so after all declared
; arguments become dead.)  Calling arglist() would then produce, at best,
; meaningless results.  If no fixed parameters are needed, use arglist() as
; follows:
;
;	f(dummy)
;	int dummy;			/* Type irrelevant	*/
;	{	...
;		alist = arglist(dummy);
;		...
;	}

.ENTRY	arglist,^M<>
	MOVL		8(FP),R0		;R0 <- Caller's AP
	RET					;Easy enough

---------------------------Extract from VMS.H---------------------------------
/*
 * Fundamental datatypes
 */
typedef int		INT_32;
typedef short		INT_16;
typedef char		INT_8;
typedef char		*ADDR;
typedef char		BYTE;
typedef short		WORD;
typedef int		LONG;
typedef unsigned char	UBYTE;
typedef unsigned short	UWORD;
typedef unsigned int	ULONG;
typedef BYTE		QUADWORD[8];
typedef BYTE		OCTAWORD[16];

typedef struct arglist				/* Argument list	*/
{	UBYTE count;
	BYTE mbz[3];
	LONG args[];
} ARGLIST;
-------