[comp.std.c] Reality check

albaugh@dms.UUCP (Mike Albaugh) (10/19/90)

From article <1061@dg.dg.com>, by lewine@dg-rtp.dg.com (Donald Lewine):
> OK.  See section 3.7.1: "If a function that accepts a variable number
> of arguments is defined without a parameter type list that ends with
> the ellipsis notation, the behavior is undefined."

	I see the word "defined" here, not "declared". That is, it
appears to be talking about the file containing the _code_ for
printf, rather than the file (stdio.h?) containing a declaration (or
possibly a prototype). But I'm not a language lawyer and it's more-or-less
irrelevant for reasons outlined below.

> This allows the compiler to generate a different calling sequence
> for functions with a fixed number of argument and a variable number
> of arguments.  See section 4.8 in the rationale.

	It may allow this, if it refered to declaration, but it might not
be in an implementor's best interest to take advantage of this "feature".

> Well, if the implementation uses one calling sequence (say, with
> arguments in registers) for functions with a known number of arguments
> and uses a different calling sequence (say, with arguments on the
> stack) for functions with a variable number of arguments, the 
> generated code will not call the library correctly unless the function
> is correctly declared.  I will admit that you may not get an error 
> message, but the code will not work!

	And neither will vast quantities of old code, which are used to
"rolling their own" varargs routines. In practice I believe a compiler
writer will do as I did (in a re-target of GCC)

	a) Have the means for detecting "varargsish" stuff, like taking
	   the address of a parameter.
	b) "forcing" that parameter and any following out of the regs
	   and onto the stack in the "prologue" of the called routine.

	It is darn hard (not impossible) to "fool" this approach, and
not too hard to implement it (I hacked my prologue generation in GCC
for a machine with the first five params passed in regs). It is also
a whole lot easier on the nerves than explaining to everyone with
old code why "my compiler meets the standard nyah, nyah" :-)

> Note also that the definition of ANSI C was done to allow a compiler
> to generate calls with arguments in registers and several new RISC
> compilers take advantage of that.

	As does my version of gcc, but it takes steps to avoid blowing
up on really common old idioms.

					Mike

BTW: to those who are interested, no, the obvious hooks for this do _not_
work (on 1.37), but it _can_ be done with less obvious hooks and version
2 is supposed to make it easier. That is not, however a subject for
comp.std.c.

| Mike Albaugh (albaugh@dms.UUCP || {...decwrl!pyramid!}weitek!dms!albaugh)
| Atari Games Corp (Arcade Games, no relation to the makers of the ST)
| 675 Sycamore Dr. Milpitas, CA 95035		voice: (408)434-1709
| The opinions expressed are my own (Boy, are they ever)

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/20/90)

In article <1171@dms.UUCP> albaugh@dms.UUCP (Mike Albaugh) writes:
>	And neither will vast quantities of old code, which are used to
>"rolling their own" varargs routines.

That never was a very wise practice, and <varargs.h> was devised LONG AGO
to provide portable support for such situations.  I lost track of how
many thousands of lines of other people's source code I have had to
change to use <varargs.h> when porting to an environment different from
the model that they assumed when they "rolled their own" variable-
argument code.

I expect most conforming implementations to also provide <varargs.h> as
a convenience for their customers, although some may well require the
extra hooks that the standard provides and thus really not be able to
implement <varargs.h>, only <stdarg.h>.

scs@adam.mit.edu (Steve Summit) (10/20/90)

In article <1171@dms.UUCP> albaugh@dms.UUCP (Mike Albaugh) writes:
>From article <1061@dg.dg.com>, by lewine@dg-rtp.dg.com (Donald Lewine):
>> Well, if the implementation uses one calling sequence (say, with
>> arguments in registers) for functions with a known number of arguments
>> and uses a different calling sequence (say, with arguments on the
>> stack) for functions with a variable number of arguments, the 
>> generated code will not call the library correctly unless the function
>> is correctly declared.
>
>In practice I believe a compiler
>writer will do as I did (in a re-target of GCC)
>	a) Have the means for detecting "varargsish" stuff, like taking
>	   the address of a parameter.
>	b) "forcing" that parameter and any following out of the regs
>	   and onto the stack in the "prologue" of the called routine.
>	It is darn hard (not impossible) to "fool" this approach, and
>not too hard to implement it (I hacked my prologue generation in GCC
>for a machine with the first five params passed in regs).

Note that another optimization which the standard now allows,
which I expect compiler writers to begin taking advantage of, is
to use a calling sequence which pops arguments in the callee for
non-varargs functions.  A frequent complaint about C by the
blizzard-efficiency aficionados is that the former implicit
requirement that all functions might be varargs (so that printf
could exist at all) meant that the caller had to pop arguments
(since it knew how many there were).  This approach bloated code
size a bit (the stack adjustment to pop the arguments happened at
each point of call, rather than once in the function implementation)
and precluded the use of special-purpose pop-and-return instructions
provided by some architectures.

A congenial implementation that tries to use this optimization
but still allow old, now-incorrect code (that calls varargs
functions without prototypes in scope) to work cannot do so,
because even if it "recognizes" a varargs function implementation
and decides not to pop the arguments within it, there is still no
way for each call to be recognized (without, of course,
prototypes in scope) so that the arguments can be popped there.

I'm all for backwards compatibility when I can get away with it,
and it's too bad that old code that calls printf without
#including <stdio.h> is now not-strictly-conforming, but (as none
other than Dennis Ritchie has pointed out) a large former wart in
the language was that variadic functions were not allowed, yet
printf existed.  The standard fixes this, at the cost of
requiring prototyopes for variadic functions.  (Fortunately for
us old-style aficionados, they are nowhere else required, yet.)

                                            Steve Summit
                                            scs@adam.mit.edu

albaugh@dms.UUCP (Mike Albaugh) (10/22/90)

From article <1990Oct19.222246.23253@athena.mit.edu>, by scs@adam.mit.edu (Steve Summit):
> In article <1171@dms.UUCP> albaugh@dms.UUCP (Mike Albaugh) (That's me) writes:
>>	It is darn hard (not impossible) to "fool" this approach, and
>>not too hard to implement it (I hacked my prologue generation in GCC
>>for a machine with the first five params passed in regs).
> 
> Note that another optimization which the standard now allows,
> which I expect compiler writers to begin taking advantage of, is
> to use a calling sequence which pops arguments in the callee for
> non-varargs functions.

	Let's back off a bit here. I was _not_ arguing for that it was
a good idea to call a variadic function without a prototype in scope.
I was also not arguing that the hack I mentioned was the final, official,
"right answer". I _was_ arguing that on most machines currently extant,
the relatively simple expedient of forcing register-passed params onto the
stack in varargs routines is a useful part of "defensive programming"
on the part of the compiler writer. It is often also a "space win" versus
having the caller do it. I am aware of the arguments that add up to "this
will not always be possible in an otherwise conforming implementation". As
Doug Gwyn pointed out, there are some really bad "hand rolled" variadic
functions out there. The sort of folks who are going to get bent out of
shape on a "non mainstream" implementation have more problems than calling
printf() without a prototype.

	_If_ someone has a good reason for wanting "callee pops args"
or for using a machine in which that is the "natural" way of doing things,
they are presumably adult enough to deal with properly prototyping
their own functions, and including the proper headers. Someone without
even that much discipline will not get far with the rest of their code (e.g.
calling a routine that returns a pointer without declaring it at all) on
such an implementation.

	_If_ on the other hand, an implementation _can_ (sans "cc"
options to the contrary) "do the right thing" to old, crufty, incorrect
code relatively easily it should. If it gives a diagnostic, so much the
better. But to just say "the standard says I don't have to, so go suck
a rock" is IMHO, a poor example of "quality of implementation". The
standard is a "contract" between the implementor and the user, but I would
_like_ to think there are more programmers involved here than lawyers :-).

	I am reminded of what happened to a friend of mine, whose company
had a new building constructed. The contract specified "Builder shall supply
lighting fixtures conforming to specification [on another page] at 10'
intervals in all hallways..." with the appropriate drawings referenced
and marked. When they took delivery of the building, there were _boxed_
light fixtures sitting along the walls in each of the referenced places.
Seems the _correct_ language was "Builder shall supply _and install_..."
Do we _really_ want to follow in this fine American legal tradition of
"Ha, Ha, got you!" ?


	RECAP: (All these are my _OPINIONS_ about "quality implementations"

	1) It is not too much to ask to require a definition of a variadic
function to use stdarg.

	2) Where it would be a real hassle to the implementor, it is not
unreasonable to require a declaration to include a prototype as well, but
such a requirement should be prominently pointed out to prospective purchasers.

	3) If the "customer" wants the benefit of such potentially dangerous
optimizations as non-widened parameters and "callee pops", he/she should have
to _ask_ for them, by including a prototype or using a compiler "switch" or
the like. This is unlikely to be a useful approach when calling variadic
library routines, unless the user has a great deal of discipline. Thus it
becomes a case of "2" above.

	Oh, yeah, I reserve a special place in hell for implementors
whose _own_ headers (and libraries) choke their own compilers. You
know who you are :-(

					Mike

| Mike Albaugh (albaugh@dms.UUCP || {...decwrl!pyramid!}weitek!dms!albaugh)
| Atari Games Corp (Arcade Games, no relation to the makers of the ST)
| 675 Sycamore Dr. Milpitas, CA 95035		voice: (408)434-1709
| The opinions expressed are my own (Boy, are they ever)

meissner@osf.org (Michael Meissner) (10/23/90)

In article <14199@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn)
writes:

| In article <1171@dms.UUCP> albaugh@dms.UUCP (Mike Albaugh) writes:
| >	And neither will vast quantities of old code, which are used to
| >"rolling their own" varargs routines.
| 
| That never was a very wise practice, and <varargs.h> was devised LONG AGO
| to provide portable support for such situations.  I lost track of how
| many thousands of lines of other people's source code I have had to
| change to use <varargs.h> when porting to an environment different from
| the model that they assumed when they "rolled their own" variable-
| argument code.
| 
| I expect most conforming implementations to also provide <varargs.h> as
| a convenience for their customers, although some may well require the
| extra hooks that the standard provides and thus really not be able to
| implement <varargs.h>, only <stdarg.h>.

With the MIPS standard calling sequence (also used in DECstations, and
Silicon Graphics systems), there is one case that is impossible with
varargs, but can be handled with stdarg.  If the first argument is
floating point, the MIPS calling sequence passes the number in a
floating point register, otherwise it passes the first argument in
integer registers.  If the second argument was floating point and the
first one was as well, it too is passed in a floating point register,
otherwise it is passed in an integer register.  The varargs stuff only
works for arguments passed in integer registers.  Because varargs
functions don't tell the compiler anything in terms of a prototype,
the compiler doesn't have a clue that it should really pass the
floating point arguments in an integer register.

The one thing that varargs can do which stdarg can't, is vary the type
of the first parameter, since stdarg requires at least one
non-varardic argument to hook into.  In the ANSI meetings this was
brought up and dismissed due to thinking the case was artifical, and
that no real code actually dependend on it.  Well we just found that
one OS test suite does in fact depend on this, and only passes one
argument to a varardic function, using other means to tell what type
the argument is.  In a similar vein, you can't have a stdarg function
which takes zero or more arguments....


--
Michael Meissner	email: meissner@osf.org		phone: 617-621-8861
Open Software Foundation, 11 Cambridge Center, Cambridge, MA, 02142

Do apple growers tell their kids money doesn't grow on bushes?

gwyn@smoke.brl.mil (Doug Gwyn) (11/01/90)

In article <MEISSNER.90Oct22135944@osf.osf.org> meissner@osf.org (Michael Meissner) writes:
>The one thing that varargs can do which stdarg can't, is vary the type
>of the first parameter, since stdarg requires at least one
>non-varardic argument to hook into.  In the ANSI meetings this was
>brought up and dismissed due to thinking the case was artifical, and
>that no real code actually dependend on it.  Well we just found that
>one OS test suite does in fact depend on this, and only passes one
>argument to a varardic function, using other means to tell what type
>the argument is.  In a similar vein, you can't have a stdarg function
>which takes zero or more arguments....

I don't think the sole argument was that no real code would attempt that,
but rather that the hook may well be necessary for some implementations.
Obvious it would have been nicer if we could have required support for
"function(...)", but there were counterexamples so we ended up needing to
require a fixed "anchor" for the va_start() macro to grab hold of.

What OS test suite is that?  If it's the NIST one for 1003.1, no wonder.
I've heard nothing good about that suite.

blarson@dianne.usc.edu (bob larson) (11/01/90)

In article <MEISSNER.90Oct22135944@osf.osf.org> meissner@osf.org (Michael Meissner) writes:
>The one thing that varargs can do which stdarg can't, is vary the type

Another big advantage to varargs is it can, on some systems, be implented
without source to the C compiler.  I understand why this was not of
concern to ANSI, but it is to those of us who need varargs/stdargs
functionality on systems without ANSI C compilers.

(I've written a copuple of varargs implementations.)
-- 
Bob Larson (blars)	blarson@usc.edu			usc!blarson
	Hiding differences does not make them go away.  Accepting
	differences makes them unimportant.

karl@ima.isc.com (Karl Heuer) (11/06/90)

In article <27820@usc> blarson@dianne.usc.edu (bob larson) writes:
>Another big advantage to varargs is it can, on some systems, be implented
>without source to the C compiler.  I understand why this was not of
>concern to ANSI, but it is to those of us who need varargs/stdargs
>functionality on systems without ANSI C compilers.

I've had no trouble implementing <stdarg.h> without touching any compiler
source.  It even works on pre-ANSI compilers, so I can take code written with
ANSI prototypes and <stdarg.h> and simply deprotoize it without having to
worry about converting <stdarg.h> to <varargs.h>.

Karl W. Z. Heuer (karl@ima.isc.com or uunet!ima!karl), The Walking Lint

gargulak@mozart.convex.com (Tom Gargulak) (11/07/90)

In article <1990Nov05.233623.11491@dirtydog.ima.isc.com>,
karl@ima.isc.com (Karl Heuer) writes:
> 
> I've had no trouble implementing <stdarg.h> without touching any compiler
> source.  It even works on pre-ANSI compilers, so I can take code written with
> ANSI prototypes and <stdarg.h> and simply deprotoize it without having to
> worry about converting <stdarg.h> to <varargs.h>.

Sorry, but I cannot see anyway you could implement "..." as in:

	foo(arg1, ...)

on a pre-ANSI compiler.   How'd you do it?


-Tom 

karl@ima.isc.com (Karl Heuer) (11/15/90)

In <108329@convex.convex.com> gargulak@mozart.convex.com (Tom Gargulak) writes:
>karl@ima.isc.com (Karl Heuer) writes:
>>[My <stdarg.h>] even works on pre-ANSI compilers, so I can take code written
>>with ANSI prototypes and <stdarg.h> and simply deprotoize it without having
>>to worry about converting <stdarg.h> to <varargs.h>.
>
>[What about ellipsis?]

The ellipsis is part of the function prototype, and so is removed by the
deprotoizer:
	int printf(char const *fmt, ...) {  /* as stored in source file */
	int printf(fmt) char *fmt; {        /* after deproto and -Dconst="" */
which is acceptable to the Classic C compiler.  <stdarg.h> says
	#define va_start(ap, parmN) (ap = (char *)&parmN + sizeof(parmN))
in the typical implementation.

Karl W. Z. Heuer (karl@ima.isc.com or uunet!ima!karl), The Walking Lint