[comp.sys.dec] ANSI-C style variadic functions on MIPS

raeburn@athena.mit.edu (Ken Raeburn) (01/05/90)

(I apologize if this issue has already been raised, but I haven't been
reading these newsgroups.)

How does one properly implement <stdarg.h> for a PMAX?  (Or any
MIPS-based system, for that matter.)

According to "MIPS RISC Architecture" by Kane, the first few arguments
to a function are passed in registers, if their types fit certain
constraints.

If the first argument to a function is assumed to be some floating
point type, the possibilities for the argument locations boil down to:

	arguments		registers (f1->$f12 always)
	f1, f2, ...		f2->$f14, remainder on stack
	f1, n1, ...		n1->$6, remainder [elsewhere]

(Arguments following N1 don't really matter, I think.)
If a function is declared as:

	void foo (double d, ...);

then where does it find the next argument?  Is it in $6 or $f14?  Even
the simplest mechanisms I can imagine that conform to the description
above involve a fair amount of compiler support, which isn't being
provided so far as I can see.

The best working alternative I see is that arguments which may have
different types get treated as type 'N' arguments in the table above,
even if they are floating-point values.  This isn't what is described
in Kane's book, but I don't see any other way to make it work.  (This
also is not what the MIPS compiler does, but a simple test file shows
that the stdarg use simply fails, so....)

(Before someone points it out, I am aware that the compiler on the
PMAX does not claim to be ANSI compliant.)

Anybody know any "official" answers?

-- Ken

grunwald@foobar.colorado.edu (Dirk Grunwald) (01/06/90)

You might want to check the documentation for the Gnu C compiler; it
documents a little state machine that implements the MIPS convention.
Also, check stdarg.h & the version of varargs for the mips (va-mips.h).

recall that the mips calling convention always allocates 4 words on
the frame. In general, if you're going to pass varidic args to
something like printf, the ``varargs'' package flushs the registers to
memory. I think this also happens if you take the address of an
argument.

raeburn@athena.mit.edu (Ken Raeburn) (01/06/90)

In article <15370@boulder.Colorado.EDU> grunwald@foobar.colorado.edu writes:
>You might want to check the documentation for the Gnu C compiler; it
>documents a little state machine that implements the MIPS convention.

I didn't mention it in my previous posting, but in my test file (using
stdarg.h and a function taking "(double, ...)"), gcc botches it even
worse than the standard compiler.  At least with /bin/cc I can read
integer arguments following the double; with gcc I get zeros for
integers as well as doubles (following the explicitly declared double
argument, that is).

The MIPS configuration files are fairly conservative about following
the rules about passing arguments and writing back all the interesting
registers to the stack for functions that are even suspected of using
varargs, but they don't do much about retrieving the information.

>Also, check stdarg.h & the version of varargs for the mips (va-mips.h).

These macros assume that the compiler will write back the registers to
the stack, or that (in the case of the GNU version, on some
architectures) there's a __builtin_saveregs available that can be
called to do that.  That doesn't tell you where you are in the
argument list.  I.e., if the first argument you try to retrieve is a
double, will it be on the stack or in registers?

The varargs version seems to make use of the idea that you're supposed
to use "va_alist" as the only thing(s) in the argument list, so you
know where you're starting from, and you know when to switch from
gee-which-registers-do-I-use-for-this-one to now-I-look-on-the-stack.
With stdarg.h, you don't have this; the state machine doesn't help if
you don't know what state to start in.

And as I think I mentioned in my previous posting, I don't think
stdarg.h works as provided.  (In fact, a large part of my reason for
posting is that I'd like to get a stdarg.h that works with gcc on the
pmax.)

>	 In general, if you're going to pass varidic args to
>something like printf, the ``varargs'' package flushs the registers to
>memory.

Yes, but varargs is in better control in this respect.
It may be less efficient than letting the programmer specify the types
of leading arguments, but the same constraints make it easier to
locate the arguments -- e.g., by forcing all registers that could be
used as arguments to be written out to memory in a known format, with
a known variable located at a known position within that block.

Maybe I should point it out again:  The troublesome case is a function
declared:
	foo (double d, ...)

How does it know, when you ask for a "double", that it should look at
the second FP reg save slot?  All that's available to the macros is
the address of d; but since the macros can't tell the difference
between foo's declaration above and
	foo2 (double d1, double d2, ...)
(for which it should start with the first stack argument) how does it
know when to switch from FP reg save slots to regular stack arguments?
It needs to know, if they aren't contiguous in memory.

I should rephrase that: How _should_ it know?  My claim is that
(lacking more information from the compiler) it does not.

-- Ken

lai@mips.COM (David Lai) (01/07/90)

In article <1990Jan6.062805.23863@athena.mit.edu> raeburn@athena.mit.edu (Ken Raeburn) writes:
>
>Maybe I should point it out again:  The troublesome case is a function
>declared:
>	foo (double d, ...)
>
>How does it know, when you ask for a "double", that it should look at
>the second FP reg save slot?  All that's available to the macros is
>the address of d; but since the macros can't tell the difference
>between foo's declaration above and
>	foo2 (double d1, double d2, ...)
>(for which it should start with the first stack argument) how does it
>know when to switch from FP reg save slots to regular stack arguments?
>It needs to know, if they aren't contiguous in memory.
>
>I should rephrase that: How _should_ it know?  My claim is that
>(lacking more information from the compiler) it does not.
>
>-- Ken

The MIPS compiler does support full stdarg.h type calls, even if the first
argument is a float (or double).  The secret is that the prototype must be
*visible* at the caller side (see ANSI section 4.8.1.3).  The compiler
knows where the beginning of the variable part of the argument list is
(the position of the '...' in the prototype), and hence forces arguments
to be passed in the integer registers (even arguments that are
float or double).  The actual function must also be *defined* with the
same prototype form, including the '...'.

Example:

main()
 {
 extern foo(double,...);
 foo(2.0,3.0);  /* passes 2.0 in fp reg, 3.0 in regular registers */
 foo2(2.0,3.0); /* passes 2.0, 3.0 in fp regs */
 }

We are aware of limitations of the old style varargs.h type calls when
the first argument is a float or double.
-- 
        "What is a DJ if he can't scratch?"  - Uncle Jamms Army
     David Lai (lai@mips.com || {ames,prls,pyramid,decwrl}!mips!lai)

raeburn@athena.mit.edu (Ken Raeburn) (01/08/90)

In article <34218@mips.mips.COM> lai@mips.COM (David Lai) writes:
> extern foo(double,...);
> foo(2.0,3.0);  /* passes 2.0 in fp reg, 3.0 in regular registers */

Ah, excellent.  Thank you for the confirmation.  I had thought this
was the solution, but it doesn't match Kane's book (well, maybe if you
interpret variadic-function arguments as coming under "anything else",
regardless of type -- but that should be explicitly stated) or the
behavior I saw from the PMAX (excuse me, DECstation) compiler.  (I did
have my definition -- with ellipsis -- before the call; that should be
enough.)  Could the Ultrix version be an older compiler, that didn't
have this fixed?  Theirs is 1.31, and puts 3.0 (from the example
above) in $f14, as does the current gcc port.  If it is outdated, I'll
complain at them....  What's the current one supposed to be?

Thanks again...

-- Ken

jg@max.crl.dec.com (Jim Gettys) (01/08/90)

The MIPS based systems we (Digital) sells right now are using 1.31 compilers.

As you might expect, later compilers are in field test....
				- Jim

lai@mips.COM (David Lai) (01/10/90)

In article <1990Jan8.041547.18259@athena.mit.edu> raeburn@athena.mit.edu (Ken Raeburn) writes:
>In article <34218@mips.mips.COM> lai@mips.COM (David Lai) writes:
>> extern foo(double,...);
>> foo(2.0,3.0);  /* passes 2.0 in fp reg, 3.0 in regular registers */
>
>Ah, excellent.  Thank you for the confirmation.  I had thought this
>was the solution, but it doesn't match Kane's book (well, maybe if you

A new update to Kanes book is in the works.  There is also updates
to the Languages Programmers Guide to reflect stdarg style functions.

>interpret variadic-function arguments as coming under "anything else",
>regardless of type -- but that should be explicitly stated) or the
>behavior I saw from the PMAX (excuse me, DECstation) compiler.  (I did
>have my definition -- with ellipsis -- before the call; that should be
>enough.)  Could the Ultrix version be an older compiler, that didn't
>have this fixed?  Theirs is 1.31, and puts 3.0 (from the example
>above) in $f14, as does the current gcc port.  If it is outdated, I'll
>complain at them....  What's the current one supposed to be?

The latest released compiler is 2.0.  2.10 is in beta test, which has the
stdarg fully working.
-- 
        "What is a DJ if he can't scratch?"  - Uncle Jamms Army
     David Lai (lai@mips.com || {ames,prls,pyramid,decwrl}!mips!lai)