lai@vedge.UUCP (David Lai) (04/08/88)
Perhaps this has been harped on before, but I seem to have discovered some interesting differences between 2 C compilers (sun3 and sun4) that make varargs not so portable. If you know of any 'portable' varargs implementations, please let me know. The sun3 compiler: arguments are passed in a linear block of memory (on the stack) chars and shorts are widened to int, and narrowed* in the function structures are passed on the stack subject to: 1) If the size of the structure is less than 4 then it will be padded on lower addresses and passed as size 4, sizeof in the function will not report the padding 2) If the structure size is 4 or more, it will be passed unpadded. floats are widedened to double, but not narrowed in the function everything else passed as is * narrowed means the sizeof operator reports a the argument's size smaller than the argument's real stack usage. This is important because the varargs macro must calculate the real stack usage of the argument. The sun4 compiler: <I'm not sure how arguments are passed, but eventually they appear in a linear block of memory> chars and shorts are widened to int, then narrowed structures are first copied into some temporary memory, then passed as pointers. Within the function sizeof shows the actual structure size, not the pointer size. floats are widened to double, and not narrowed everything else is passed as is Varargs usage: Sun3. chars and shorts get by using va_arg(ap,int) structures size>=4 va_arg(ap,struct_type) floats va_arg(ap,double) structures size<4: declare a fake structure type (it will be size 4): struct fake{ struct struct_type a; short pad;} tmp; get the item off the argument list tmp = va_arg(ap,struct fake); assign the first field to your variable str = tmp.a; everything else use its real type va_arg(ap,type) Sun4. chars, shorts, floats as above structures (nasty) str = *(va_arg(ap,struct_type *)); everything else va_arg(ap,type) -- The views expressed are those of the author, and not of Visual Edge or Usenet David Lai (vedge!lai@oliver.cs.mcgill.edu || ...decvax!musocs!vedge!lai)
guy@gorodish.Sun.COM (Guy Harris) (04/10/88)
In article <206@vedge.UUCP>, lai@vedge.UUCP (David Lai) writes: > Perhaps this has been harped on before, but I seem to have discovered > some interesting differences between 2 C compilers (sun3 and sun4) that > make varargs not so portable. If you know of any 'portable' varargs > implementations, please let me know. How about Sun-3 and VAX? The following program: #include <varargs.h> struct goober1 { char c; }; struct goober2 { int i; int j; int k; }; main() { struct goober1 goober1_1, goober1_2; struct goober2 goober2_1, goober2_2; goober1_1.c = 11; goober2_1.i = 1; goober2_1.j = 2; goober2_1.k = 3; goober1_2.c = 22; goober2_2.i = 100; goober2_2.j = 200; goober2_2.k = 300; varargs_func(2, goober1_1, 17, goober2_1, 34); varargs_func(3, goober1_1, 17, goober2_1, 34, goober1_2, 51); varargs_func(4, goober1_1, 17, goober2_1, 34, goober1_2, 51, goober2_2, 68); } int varargs_func(va_alist) va_dcl { va_list ap; register int i; register int count; struct goober1 one; struct goober2 two; register int num; va_start(ap); count = va_arg(ap, int); printf("count = %d\n", count); for (i = 0; i < count; i++) { if (!(i&01)) { one = va_arg(ap, struct goober1); printf("goober1: c = %d\n", one.c); } else { two = va_arg(ap, struct goober2); printf("goober2: i = %d, j = %d, k = %d\n", two.i, two.j, two.k); } num = va_arg(ap, int); printf("num = %d\n", num); } } gives different answers on a VAX running 4.3BSD and a Sun-3 running SunOS 4.0 - *neither* of which is correct! (A VAX running System V will probably give the same wrong answer as a VAX running 4.3BSD; a VAX running VMS may very well give a different answer.) You see, on the VAX, "sizeof (struct goober1)" is 1, while on the Sun-3 (and, I suspect, on most other 68K boxes), "sizeof (struct goober1)" is 2. It appears, however, that *both* compilers align arguments pushed onto the stack on 4-byte boundaries, so that the value of "ap" should be bumped by 4 when fetching a "struct goober1", not by 1 (as is done on the VAX) or 2 (as is done on the Sun-3 and probably on other 68Ks). Another way of putting this is "passing structures to varargs functions doesn't work, in general". Sad, but true. It may be possible to fix some or all of these problems in current C; the 68K/VAX problem can be fixed by having the "va_arg" macro round the size of the object up to a multiple of 4 bytes before adding it to "ap". The problem of machines that don't pass structures simply by putting them in-line after the other arguments (what do other machines that pass arguments in registers, such as the MIPS or Pyramid, do here?) may require ANSI C to solve it; if you declare a function as being a function taking a variable number of arguments, e.g. #include <stdarg.h> ... extern int varargs_func(int count, ...); ... int varargs_func(int count, ...) { va_list ap; register int i; struct goober1 one; struct goober2 two; register int num; va_start(ap, count); ... } the compiler would know that a different calling sequence should be used and just dump all the arguments past the last "fixed" argument into memory in the form the <stdarg.h> implementation expects. > The sun3 compiler: ... > structures are passed on the stack subject to: > 1) If the size of the structure is less than 4 > then it will be padded on lower addresses > and passed as size 4, sizeof in the function > will not report the padding > 2) If the structure size is 4 or more, it will > be passed unpadded. Note that, as mentioned above, "sizeof" does report whatever padding is made to satisfy the compiler's general structure alignment rules, so on the Sun-3 structures are padded to a 2-byte boundary and that padding *is* reported by "sizeof". (Also note that "sizeof" should report the *same* size inside and outside the function.) > The sun4 compiler: > <I'm not sure how arguments are passed, but eventually they > appear in a linear block of memory> If you're not passing any floating-point parameters, the first 6 parameters are passed in %o0-%o5; all subsequent parameters are passed on the stack. The SunOS 4.0 and Sys4-3.2L <varargs.h> includes #if defined(sparc) # define va_alist __builtin_va_alist #endif and the appearance of "__builtin_va_alist" in the argument list tells the compiler to generate code to dump arguments onto the stack in the proper place for "va_arg" to pick them up. > structures are first copied into some temporary memory, then > passed as pointers. Within the function sizeof shows the > actual structure size, not the pointer size. Which is as it should be; "sizeof (struct foo)" should always give the size of such a structure.