#ifndef _avcall_sparc_c /*-*- C -*-*/ #define _avcall_sparc_c /** Copyright 1993 Bill Triggs, <Bill.Triggs@inrialpes.fr> Copyright 1995-1999 Bruno Haible, <bruno@clisp.org> This is free software distributed under the GNU General Public Licence described in the file COPYING. Contact the author if you don't have this or can't live with it. There is ABSOLUTELY NO WARRANTY, explicit or implied, on this software. **/ /*---------------------------------------------------------------------- !!! THIS ROUTINE MUST BE COMPILED gcc -O !!! Foreign function interface for a Sun4 Sparc with gcc/sun-cc. This calls a C function with an argument list built up using macros defined in av_call.h. Sparc Argument Passing Conventions The first 6 words of arguments are passed in integer registers o0-o5 regardless of type or alignment. (Registers are windowed: o0-o5 become i0-i5 if the called function executes a `save' instruction.) Remaining arguments are pushed onto the stack starting at a fixed offset ("argframe"). Space is left on the stack frame for temporary storage of the register arguments as well. Doubles may be cut in half and misaligned. Shorter integers are always promoted to word-length. Functions with K&R-style declarations and float args pass them as doubles and truncate them on function entry. Structures are passed as pointers to a local copy of the structure made by the caller. Integers and pointers are returned in o0, floats in f0, doubles in f0/f1. If the function returns a structure a pointer to space allocated by the caller is pushed onto the stack immediately before the function arguments. Gcc without -fpcc-struct-return returns <= 4 byte structures as integers. Sun cc allocates temporary space for a returned structure just below the current frame pointer $fp (the $sp of the caller), and the caller must copy them from there. It also returns the temp address in $o0, but that gets nuked in the return in the code below so we can't use it. **The Sun cc struct return stuff below is a kludge**, but seems to work on the test cases... Compile this routine with gcc for the __asm__ extensions and with optimisation on (-O or -O2 or -g -O) so that argframe is set to the correct offset. (%sp is used differently in non-optimized code). For Sun cc, use the pre-compiled assembler version of this routine. ----------------------------------------------------------------------*/ #include "avcall.h.in" #define RETURN(TYPE,VAL) (*(TYPE*)l->raddr = (TYPE)(VAL)) register void* callee __asm__("%g2"); /* any global or local register */ register __avword o0 __asm__("%o0"); register __avword o1 __asm__("%o1"); register __avword o2 __asm__("%o2"); register __avword o3 __asm__("%o3"); register __avword o4 __asm__("%o4"); register __avword o5 __asm__("%o5"); int __builtin_avcall(av_alist* l) { /*?? We probably need to make space for Sun cc struct return somewhere here. */ register __avword* sp __asm__("%sp"); /* C names for registers */ register float fret __asm__("%f0"); /* %f0 */ register double dret __asm__("%f0"); /* %f0,%f1 */ __avword trampoline[6]; /* room for a trampoline */ __avword space[__AV_ALIST_WORDS]; /* space for callee's stack frame */ __avword *argframe = sp + 17; /* stack offset for argument list */ int arglen = l->aptr - l->args; __avword i; if ((l->rtype == __AVstruct) && !(l->flags & __AV_SUNCC_STRUCT_RETURN)) argframe[-1] = (__avword)l->raddr; /* push struct return address */ { int i; for (i = 6; i < arglen; i++) /* push excess function args */ argframe[i] = l->args[i]; } if ((l->rtype == __AVstruct) && (l->flags & __AV_SUNPROCC_STRUCT_RETURN)) /* SUNWspro cc compiled functions don't copy the structure to the area * pointed to by argframe[-1] unless the caller has a proper "unimp n" * instruction. We generate the calling instructions on the stack. */ { trampoline[0] = 0x9FC08000; /* call %g2 */ trampoline[1] = 0x01000000; /* nop */ trampoline[2] = l->rsize & 0xFFF; /* unimp n */ trampoline[3] = 0xB0102000; /* mov 0,%i0 */ trampoline[4] = 0x81C7E008; /* ret */ trampoline[5] = 0x81E80000; /* restore */ __asm__ __volatile__ ("iflush %0" : : "r" (&trampoline[0])); __asm__ __volatile__ ("iflush %0" : : "r" (&trampoline[2])); __asm__ __volatile__ ("iflush %0" : : "r" (&trampoline[4])); __asm__ __volatile__ ("iflush %0" : : "r" (&trampoline[5])); o0 = l->args[0]; o1 = l->args[1]; o2 = l->args[2]; o3 = l->args[3]; o4 = l->args[4]; o5 = l->args[5]; callee = l->func; goto *(void*)trampoline; } /* call function with 1st 6 args */ i = ({ __avword iret; /* %o0 */ iret = (*l->func)(l->args[0], l->args[1], l->args[2], l->args[3], l->args[4], l->args[5]); asm ("nop"); /* struct returning functions skip this instruction */ iret; }); /* save return value */ if (l->rtype == __AVvoid) { } else if (l->rtype == __AVword) { RETURN(__avword, i); } else if (l->rtype == __AVchar) { RETURN(char, i); } else if (l->rtype == __AVschar) { RETURN(signed char, i); } else if (l->rtype == __AVuchar) { RETURN(unsigned char, i); } else if (l->rtype == __AVshort) { RETURN(short, i); } else if (l->rtype == __AVushort) { RETURN(unsigned short, i); } else if (l->rtype == __AVint) { RETURN(int, i); } else if (l->rtype == __AVuint) { RETURN(unsigned int, i); } else if (l->rtype == __AVlong) { RETURN(long, i); } else if (l->rtype == __AVulong) { RETURN(unsigned long, i); } else if (l->rtype == __AVlonglong || l->rtype == __AVulonglong) { ((__avword*)l->raddr)[0] = i; ((__avword*)l->raddr)[1] = o1; } else if (l->rtype == __AVfloat) { /* old Sun cc returns floats as doubles */ if (l->flags & __AV_SUNCC_FLOAT_RETURN) { RETURN(float, (float)dret); } else { RETURN(float, fret); } } else if (l->rtype == __AVdouble) { RETURN(double, dret); } else if (l->rtype == __AVvoidp) { RETURN(void*, i); } else if (l->rtype == __AVstruct) { /* This is a kludge for old Sun cc and is probably fragile. */ if (l->flags & __AV_SUNCC_STRUCT_RETURN) { /* Sun cc struct return convention */ if (l->rsize == sizeof(char)) { RETURN(char, ((char*)sp)[-1]); } else if (l->rsize == sizeof(short)) { RETURN(short, ((short*)sp)[-1]); } else if (l->rsize == sizeof(int)) { RETURN(int, ((int*)sp)[-1]); } else if (l->rsize == sizeof(double)) { ((int*)l->raddr)[0] = ((int*)sp)[-2]; ((int*)l->raddr)[1] = ((int*)sp)[-1]; } else if (l->rsize % 4) { char* dstaddr = (char*)l->raddr; char* srcaddr = (char*)((long)sp - l->rsize); unsigned int count = l->rsize; if (count > 4) srcaddr = (char*)((long)srcaddr & -4); while (count > 0) { *dstaddr++ = *srcaddr++; count--; } } else { __avword* dstaddr = (__avword*)l->raddr; __avword* srcaddr = (__avword*)((long)sp - l->rsize); while (srcaddr < sp) *dstaddr++ = *srcaddr++; } } else { if (l->flags & __AV_PCC_STRUCT_RETURN) { /* pcc struct return convention: need a *(TYPE*)l->raddr = *(TYPE*)i; */ if (l->rsize == sizeof(char)) { RETURN(char, *(char*)i); } else if (l->rsize == sizeof(short)) { RETURN(short, *(short*)i); } else if (l->rsize == sizeof(int)) { RETURN(int, *(int*)i); } else if (l->rsize == sizeof(double)) { ((int*)l->raddr)[0] = ((int*)i)[0]; ((int*)l->raddr)[1] = ((int*)i)[1]; } else { int n = (l->rsize + sizeof(__avword)-1)/sizeof(__avword); while (--n >= 0) ((__avword*)l->raddr)[n] = ((__avword*)i)[n]; } } else { /* normal struct return convention */ if (l->flags & __AV_SMALL_STRUCT_RETURN) { if (l->rsize == sizeof(char)) { RETURN(char, i); } else if (l->rsize == sizeof(short)) { RETURN(short, i); } else if (l->rsize == sizeof(int)) { RETURN(int, i); } } } } } return 0; } #endif /*_avcall_sparc_c */