Article View: gnu.gcc.help
Article #53254using gcc with dbm on sparc +FIX for gas-1.38 (SPARC, input="unimp 0")
From: Alexander Dupuy
Date: Fri, 21 Feb 1992 09:44
Date: Fri, 21 Feb 1992 09:44
226 lines
9095 bytes
9095 bytes
> Our ndbm seems to do little more than core dump. I am sitting-in on the
> networks class and trying to get some simple ndbm stuff to work, but can't.
> A number of other students in the class have started to send me mail, having
> the same problem.
>
> I compiled the gdbm library, and tried running some of their test programs,
> which also dumped core.
>
> It seems that if I switch from using gcc to plain cc, everything works?????
The problem is that the {n,g,}dbm libraries all pass structs as arguments, and
return structs as well. But gcc and Sun cc have different conventions about
passing aggregates (structs or unions) as arguments and returning them.
Here's a (probably too long for most people) explanation of why this is, and
what you can do about it if you want use gcc with the {n,g,}dbm libraries
compiled with Sun cc:
Sun cc passes all aggregates by copying the aggregate onto the stack and
passing a pointer to this copy.
On the other hand, gcc will pass as much of the aggregate as possible in
registers (on a SPARC, up to 24 bytes) and the rest on the stack.
There is a hack you can use in gcc-compiled code which needs to call
Sun-compiled routines that expect to be passed aggregates, which is to make a
copy of the aggregate and pass a pointer to that. Be aware that the called
function may modify the copy you have made. If you don't care, you don't need
to copy the aggregate, and your code will be even more efficient.
Aggregate return on SPARC machines is much more complicated and ugly. There's
a good description of the Sun cc approach in the Summer 1988 SunTechnology, but
the gist is this: when Sun cc generates a call to a routine which returns an
aggregate, the caller does these things:
allocates storage on the stack for the returned aggregate
passes a hidden argument (in a special location in the stack frame)
which points to the allocated storage
calls the routine
the instruction after the (delay slot for the) call is an unimp
(unimplemented instruction) containing the aggregate size
copies the returned aggregate to wherever it's going to go
and the callee does the following things:
allocates even more storage for the aggregate on the stack
[ then does whatever it wants to with the aggregate ]
calls a routine (.stret1, .stret2, or .stret4, depending on alignment)
and .stretX then does:
checks for the unimp instruction containing the aggregate size
if it doesn't match, just does a regular return
else, copies the aggregate from callee's frame to caller's frame
returns to the instruction after the unimp
Note that if a caller declares an aggregate-returning function as a regular
function, the function will not return an aggregate. Vice versa, if a caller
declares a regular function as an aggregate-returning function, it will
generate an "Illegal Instruction" trap when it tries to return. This will also
happen if Sun-compiled code calls a gcc-compiled aggregate-returning function.
Of course, gcc does it differently; when it generates a call to a routine which
returns an aggregate, the caller does these things:
passes a hidden argument (in a special location in the stack frame)
which points to the place where the result should go
calls the routine
and the callee does the following things:
allocates storage for the aggregate on the stack
[ then does whatever it wants to with the aggregate ]
copies the aggregate into the storage pointed to by the hidden argument
In this case, if a caller declares an aggregate-returning function as a regular
function, you are likely (but not guaranteed) to get a core dump when the
callee tries to use the (uninitialized) hidden argument, a bug which could be
hard to track down. Vice versa, if a caller declares a regular function as an
aggregate-returning function, nothing will be returned. This will also happen
if gcc-compiled code calls a Sun-compiled aggregate-returning function (since
the unimp instruction will be missing).
So the behavior of gcc-compiled code is the reverse of Sun-compiled code,
except for the indeterminate core dump in code miscompiled using gcc, and the
fact that gcc-compiled code is much more efficient.
Working around this bizarre little convention of using unimplemented
instructions is a bit trickier than the simple case of structure passing, but
with a bit of ingenuity (and a bug-fix for the GNU assembler), it is possible.
[Note that while there is a flag for gcc called "-fpcc-struct-return", it has
no effect on SPARC architectures - it's only for compatibility with the old
PCC convention of returning a pointer to static storage, which was never
implemented on the SPARC architecture.]
The trick is to use the gcc "extended assembler" feature to insert the unimp
opcode after the (delay slot for the) function call, and the following two
macros do exactly that, with slightly different syntactic sugar:
#ifdef __sparc__
#ifdef __GNUC__
#define _struct_ret(expr) \
{ expr; __asm ("unimp %0&0xfff" : : "i" (sizeof (__typeof (expr)))); }
#else
#define _struct_ret(expr) { expr; }
#endif
#else
#define _struct_ret(expr) { expr; }
#endif
#ifdef __sparc__
#ifdef __GNUC__
#define _struct_ret2(lval,call) \
{ (lval) = (call); __asm ("unimp %0&0xfff" : : "i" (sizeof (lval))); }
#else
#define _struct_ret2(lval,call) { (lval) = (call); }
#endif
#else
#define _struct_ret2(lval,call) { (lval) = (call); }
#endif
An example of their use with dbm:
#include <dbm.h>
main (argc, argv)
int argc;
char **argv;
{
datum key;
datum value;
datum result;
key.dptr = "key";
key.dsize = strlen (key.dptr) + 1;
value.dptr = "value";
value.dsize = strlen (value.dptr) + 1;
dbminit (argc > 1 ? argv[1] : "dbmfile");
result = key; /* copy key - it may be modified by store */
store (&result, &value);/* using hack for structure arguments */
_struct_ret2 (result, firstkey ());
printf ("first key is %s\n", result.dptr);
value = key; /* copy key - it may be modified by fetch */
_struct_ret (result = fetch (&value));
printf ("fetch result was %s\n", result.dptr);
}
Now there's just one catch to this neat little hack (if I say so myself :-)
which is that it doesn't work with gas, the GNU assembler, because gas has a
bug which causes it to believe that the argument to unimp is PC-relative,
which, between as and ld, scrambles it pretty well. It works fine with the Sun
assembler, if your gcc is set up to use that. A fix for gas-1.38 follows:
*** 1.1 1992/02/21 08:02:50
--- sparc-opcode.h 1992/02/21 08:47:38
***************
*** 58,63 ****
--- 58,64 ----
D crsd coprocessor register
h 22 high bits.
i 13 bit Immediate.
+ n 22 bit Immediate.
l 22 bit PC relative immediate.
L 30 bit PC relative immediate.
a Annul. The annul bit is set.
***************
*** 497,503 ****
{ "tsubcctv", 0x80580000, 0x40a00000, "1,2,d", 0 },
{ "tsubcctv", 0x80582000, 0x40a00000, "1,i,d", 0 },
! { "unimp", 0x00000000, 0x00000000, "l", 0 },
{ "iflush", 0x81d80000, 0x40202000, "1+2", 0 },
{ "iflush", 0x81d82000, 0x40200000, "1+i", 0 },
--- 498,504 ----
{ "tsubcctv", 0x80580000, 0x40a00000, "1,2,d", 0 },
{ "tsubcctv", 0x80582000, 0x40a00000, "1,i,d", 0 },
! { "unimp", 0x00000000, 0xffc00000, "n", 0 },
{ "iflush", 0x81d80000, 0x40202000, "1+2", 0 },
{ "iflush", 0x81d82000, 0x40200000, "1+i", 0 },
*** 1.1 1992/02/21 08:25:26
--- sparc.c 1992/02/21 08:27:02
***************
*** 677,682 ****
--- 677,686 ----
case 'i': /* 13 bit immediate */
the_insn.reloc = RELOC_BASE13;
+ goto immediate;
+
+ case 'n': /* 22 bit immediate */
+ the_insn.reloc = RELOC_BASE22;
/*FALLTHROUGH*/
And after all that, I'll just make one little request, which is - it would be
nice if gcc-2.0 has a "-msparc-struct-return" flag which does that little bit
of magic without extended assembler macros, and which allows Sun-compiled code
to call gcc-compiled libraries by having the function epilogue skip over the
unimp opcode if one is present.
Thanks, and goodnight.
@alex
--
inet: dupuy@cs.columbia.edu
Member of the League for Programming Freedom -- write to league@prep.ai.mit.edu
Technical Details
Message-ID:
<9202211012.AA04986@news.cis.ohio-state.edu>Path:
arc1-ca.pugleaf.net!sparky!uunet!cis.ohio-state.edu!hudson.cs.columbia.edu!dupuyShow Full Headers
Complete Article Headers
X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 11318c,f46ad052c9d8235c X-Google-Attributes: gid11318c,public X-Google-ArrivalTime: 1992-02-21 03:57:29 PST Newsgroups: gnu.gcc.help Path: arc1-ca.pugleaf.net!sparky!uunet!cis.ohio-state.edu!hudson.cs.columbia.edu!dupuy From: dupuy@hudson.cs.columbia.edu (Alexander Dupuy) Subject: using gcc with dbm on sparc +FIX for gas-1.38 (SPARC, input="unimp 0") Message-ID: <9202211012.AA04986@news.cis.ohio-state.edu> Sender: help-gcc-request@prep.ai.mit.edu Reply-To: dupuy@cs.columbia.edu Organization: GNUs Not Usenet Distribution: gnu Date: Fri, 21 Feb 1992 09:44:18 GMT Lines: 226