🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.c.moderated
Article #2157

Re: Variable param for non-vararg subr. calls

#2157
From: Chris Torek
Date: Sat, 02 Mar 1996 19:38
98 lines
4797 bytes
(This article is a bit of a mishmash, but mostly tries to illustrate
some of the possible calling conventions.)

On Fri, 1 Mar 1996, I wrote (in private mail -- hey, ask before you
post! :-) ):
>> (The sparc calling convention passes structures specially -- that is,
>> the call sequence for an aggregate is different from that for any
>> scalar.)

In article <4h9mjl$grs@solutions.solon.com>
Jens M Andreasen <jens-and@dsv.su.se> writes:
>Ok!
>
>Is there a workaround for this? (No, forget it ... We have just bought a 
>bunch of them. I'll have a look at it, maybe ..)

To expound a bit: on the sparc, a function that appears to receive
a struct or union actually receives a pointer to that struct or union.
A caller who passes a struct or union must first copy the value of
the aggregate into a temporary, then pass a pointer to that temporary.

This means that the (incorrect) code:

	void f(p) struct X *p; {
		... use but do not change p->elem ...
	}

	void g() {
		struct X val;
		...
		f(val);
	}

will actually `work' on the sparc (a bit expensively since g() will
copy `val' to a temporary).

In effect, `small' arguments go in envelopes (registers), and `large'
arguments go in boxes (memory) with a slip inside an envelope telling
where to find the box.

>> Your code will also fail on MIPS-based systems, among others.

>This confuses me a bit since I usually jack into a DecStation having (to 
>my knowledge) some Mips inside ?? The sample output was taken from there.

I probably goofed here.  The MIPS passes the first four 32-bit
integer arguments in the `a' registers (a0, a1, a2, a3, also known
as $4 through $7).  I believe, but cannot verify, that the first
two `double' arguments are also passed via registers, in this case
$f0 and $f2.  If the calling convention for struct arguments is to
break them down element-wise and pass each element in the corresponding
argument slot, the technique would work in all cases.  On the other
hand, if the calling convention is to break them down into four-byte
blobs and pass them as if they were integers, the technique might
fail when the first or second argument should have type `double'.

I do not know if the MIPS calling conventions identify (and then
treat differently) variable-argument functions, and my ancient Kane
book (copyright 1988) does not go into details.  (The example code
provided a `double' argument, but it was the third argument to
printf(), so the above represents my best guesses as to the MIPS
conventions for struct arguments.)

>While I'm at it: The code do run on 68k and Intel based systems :-)

I am not sure if Intel have ever provided specific guidelines as
to calling conventions on the 80x86 platforms, but there are only
two major variants of which I am aware -- these being the so-called
`cdecl' and `pascal' conventions.  It is, however, worth noting
that any compiler that uses `pascal conventions' for fixed-argument
functions and `cdecl conventions' for variable-argument functions
could also cause this code to fail (because, e.g., a struct is a
fixed argument, and puts takes a fixed argument, but the number of
bytes of arguments between the two are unbalanced, so that the
`ret' from puts() would pop the wrong number of argument bytes;
whether this was quickly fatal would depend on whether the compiler
used a frame pointer).

680x0 platforms have also used various calling conventions, but the
most widespread variants all pass all parameters by pushing right to
left on sp@- and return all values in d0.  A few compilers have
returned pointer values in a0.  This means that you must distinguish
between `functions returning pointers' and `all others', but it does
not affect argument-passing.

Possibly the toughest calling convention would occur on a machine
that has two or more `flavors' of register, and passes arguments in
registers.  In this case, you might find that:

	void f1(int, char *)
	void f2(char *, int)

both used the same call!  Here the integer might go in d0, and the
address in a0.  Such a convention would be useful on (surprise :-) )
the 680x0.
-- 
In-Real-Life: Chris Torek, Berkeley Software Design Inc
El Cerrito, CA	Domain:	torek@bsdi.com	+1 510 234 3167
Technical Details
Message-ID: <4ha84e$js1@solutions.solon.com>
Path: arc1-ca.pugleaf.net!solon.com!not-for-mail
References: <199603012002.MAA04513@elf.bsdi.com> <4h9mjl$grs@solutions.solon.com>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!solon.com!not-for-mail
From: Chris Torek <torek@elf.bsdi.com>
Newsgroups: comp.lang.c.moderated
Subject: Re: Variable param for non-vararg subr. calls
Date: 2 Mar 1996 13:38:54 -0600
Organization: Berkeley Software Design, Inc.
Sender: clc@solutions.solon.com
Approved: clc@solutions.solon.com
Message-ID: <4ha84e$js1@solutions.solon.com>
References: <199603012002.MAA04513@elf.bsdi.com> <4h9mjl$grs@solutions.solon.com>
Reply-To: torek@bsdi.com
NNTP-Posting-Host: solutions.solon.com