#include "Gen_RPC8.h" unsigned int GenRPC::BuildStack(char *stack) { char *stackPtr = (char*) stack; SerializeHeader(stackPtr, 0); return (unsigned int)(stackPtr-stack); } /// \internal void GenRPC::Push( char*& p, char*const i, bool doEndianSwap) { (void) doEndianSwap; size_t len = strlen( i ) + 1; memcpy( (void*)p, i, len ); p += len; } /// \internal void GenRPC::Push( char*& p, const char*const i, bool doEndianSwap ) { (void) doEndianSwap; size_t len = strlen( i ) + 1; memcpy( (void*)p, i, len ); p += len; } /// \internal unsigned GenRPC::D_type( const char*const ) { return STR_PARAM; } /// \internal unsigned GenRPC::D_type( char*const ) { return STR_PARAM; } /// \internal unsigned GenRPC::D_type( float ) { return REAL_PARAM; } /// \internal unsigned GenRPC::D_type( double ) { return REAL_PARAM; } /// \internal unsigned GenRPC::D_type( long double ) { return REAL_PARAM; } /// \internal size_t GenRPC::D_size( char*const str ) { return strlen( str ) + 1; } /// \internal size_t GenRPC::D_size( char const *const str ){ return strlen( str ) + 1; } void GenRPC::SerializeHeader(char *&out, unsigned int numParams) { *out = (char) numParams; out++; //out[*writeOffset]=(char) numParams; //*writeOffset+=sizeof(unsigned char); } // // @params // call: [IN/OUT] workspace to build parameters that we will pass to function // in: [IN/OUT] is the serialized buffer - used as a temporary working for swapping // parameters. // inLength: [IN] is the length of the above // lastParam: [IN] final parameter, added onto the list // thisPtr: [IN] if not zero - the value of this (added to start of list). // // @returns: // true: parameter list created successfully. // false: if deserialization fails for some reason. // bool GenRPC::DeserializeParametersAndBuildCall( CallParams &call, char *in, unsigned int inLength, void *lastParam, void *thisPtr ) { #if AUTO_RPC_ABI NaturalWord *intCallParam = call.intParams; char *refParam = call.refParams; #if AUTO_RPC_ALLOC_SEPARATE_FLOATS HardwareReal *realCallParam = call.realParams; #endif #if AUTO_RPC_CREATE_FLOAT_MAP call.realMap = 0; call.numRealParams = 0; #endif #if AUTO_RPC_ABI == AUTO_RPC_ABI_SYSV_AMD64 // large structure parameters have to be bumped up here - which corresponds with the start // of the parameters that *are* passed on the stack. NaturalWord *memParam = &call.intParams[ AUTO_RPC_INT_REG_PARAMS ]; #endif // this is first arg - assume space ;-) #pragma warning(disable:4311) // pointer truncation if ( thisPtr ) *(intCallParam++) = reinterpret_cast( thisPtr ); unsigned int serializedArgs = *(unsigned char*)in; unsigned char* header = (unsigned char*)in + 1; unsigned char* data = &header[ serializedArgs * ( sizeof( unsigned int ) + 1 ) ]; // check we have the entire header in buffer if ( data > (unsigned char*) &in[ inLength ] ) return 0; for ( unsigned int i = 0; i < serializedArgs; i++ ) { // read heade entry int const plen = *(unsigned int*)header; header += sizeof( unsigned int ); unsigned char const flags = *( header++ ); // Some bits are not used by the current implementation. So if we find them we bail because // we clearly are not equipped to handle the data - and is there no safe way to "fail". if ( flags & RESERVED_BITS ) return 0; #ifndef __BITSTREAM_NATIVE_END if (flags & DO_ENDIAN_SWAP) { RakNet::BitStream::ReverseBytesInPlace( (unsigned char*)&plen , sizeof( plen ) ); } #endif if ( !plen || data + plen > (unsigned char*)&in[ inLength ] ) return 0; // handle null-terminated strings. if ( ( flags & PARAM_TYPE_MASK ) == STR_PARAM ) { if ( intCallParam + 1 >= AUTO_RPC_ARRAY_END( call.intParams ) ) return 0; // Check this has some sort of null termination. NB this assumes string is genuine Ascii // or UTF+8; using unicode (ie. UTF+16, UCS) could break this. if ( data[ plen - 1 ] != 0 ) return 0; // The string doesn't need to be aligned, so we leave it in place; saving a copy, and // preventing clogging up of our buffers with data. #pragma warning(disable:4311) // pointer truncation *( intCallParam++ ) = reinterpret_cast( data ); data += plen; continue; } #ifndef __BITSTREAM_NATIVE_END if (flags & DO_ENDIAN_SWAP) { RakNet::BitStream::ReverseBytesInPlace( (unsigned char*)data , plen ); } #endif // convert pointer to ref. if ( ( flags & PARAM_TYPE_MASK ) == REF_PARAM #if AUTO_RPC_PARAMETER_REFERENCE_THRESHOLD || plen > AUTO_RPC_PARAMETER_REFERENCE_THRESHOLD #endif ) { char *nextRefParam = refParam + AUTO_RPC__ALIGN_P2( plen, AUTO_RPC_REF_ALIGN ); if ( nextRefParam >= AUTO_RPC_ARRAY_END( call.refParams ) || intCallParam + 1 >= AUTO_RPC_ARRAY_END( call.intParams ) ) return 0; memcpy( refParam, data, plen ); #pragma warning(disable:4311) // pointer truncation *( intCallParam++ ) = reinterpret_cast( refParam ); refParam = nextRefParam; data += plen; continue; } // Guarantee we have space on the output stack to accommodate the parameter. NaturalWord *nextParam = (NaturalWord*)( (char*)intCallParam + AUTO_RPC_ALIGN_P2( plen, NaturalWord ) ); if ( nextParam >= AUTO_RPC_ARRAY_END( call.intParams ) ) return 0; #if AUTO_RPC_ALLOC_SEPARATE_FLOATS if ( ( flags & PARAM_TYPE_MASK ) == REAL_PARAM // long doubles, of various sizes (10, 16), all get passed on the stack && (size_t) plen <= sizeof(double) // once we've allocated all our floats, they get treated as ordinary int params && realCallParam < AUTO_RPC_ARRAY_END( call.realParams ) ) { if ( plen != sizeof( float ) && plen != sizeof( double ) ) { printf("illegal float size %d\n", plen ); // We can't handle it - it's not a real real :lol: return 0; } #ifdef __BIG_ENDIAN__ memcpy( (char*)( realCallParam + 1 ) - plen, data, plen ); #else memcpy( (char*)realCallParam, data, plen ); #endif #if !AUTO_RPC_INT_SHADOW_OF_FLOATS // we didn't use the int slot, so don't allow an advance. nextParam = intCallParam; #endif // next time, we use the next Real slot realCallParam++; } #if !AUTO_RPC_INT_SHADOW_OF_FLOATS else #endif #endif // AUTO_RPC_ALLOC_SEPARATE_FLOATS { // the processor can atomically zero-extend small types, so even with the test, // it should be faster than memcpy+memset. if ( plen == 1 ) *intCallParam = *(uint8_t*)data; // should resolve to movzx and a move else if ( plen == 2 ) *intCallParam = *(uint16_t*)data; // if network order replace use htons(), and skip EndianSwap() else if ( plen == 4 ) *intCallParam = *(uint32_t*)data; // if network order replace use htonl(), and skip EndianSwap() #if AUTO_RPC_AUTORPC_WORD == 64 else if ( plen == 8 ) *intCallParam = *(uint64_t*)data; #endif #if AUTO_RPC_ABI == AUTO_RPC_ABI_SYSV_AMD64 // // SYSV ABI: aggregates greater 16 bytes must go on the stack; // in practice, that means they can't come below AUTO_RPC_INT_REG_PARAMS when we call a function. // else if ( plen > 16 || ( plen > 8 && intCallParam == &call.intParams[ AUTO_RPC_INT_REG_PARAMS - 1] ) || ( flags & REAL_PARAM ) ) { if ( intCallParam < memParam ) { NaturalWord*const nextMemParam = (NaturalWord*)( (char*)memParam + AUTO_RPC_ALIGN_P2( plen, NaturalWord ) ); if ( nextMemParam >= AUTO_RPC_ARRAY_END( call.intParams ) ) return 0; memcpy( memParam, data, plen ); // prevent advancing the ptr slot, since we didn't use it. nextParam = intCallParam; // but advance the memparam memParam = nextMemParam; } else { memcpy( (void*)intCallParam, data, plen ); } } #endif // AUTO_RPC_ABI_SYSV_AMD64 else { // We don't need to worry about alignment, because any type that's not a whole multiple // of the natual word size will be an aggregate and that should be at the base of memory - // this is true for some PowerPC systems (see [e]) but not all. But hey, you // probably should be passing such structs by reference. // // Zeroing is also unecessary as code shouldn't be reading beyodn the bounds of the structure. // memcpy( (void*)intCallParam, data, plen ); } } #if AUTO_RPC_ABI == AUTO_RPC_ABI_SYSV_AMD64 // skip over any stored "class MEMORY" (see [b]) parameters. if ( nextParam == &call.intParams[AUTO_RPC_INT_REG_PARAMS] ) intCallParam = memParam; else #endif // advance to next output param intCallParam = nextParam; #if !AUTO_RPC_ALLOC_SEPARATE_FLOATS && AUTO_RPC_CREATE_FLOAT_MAP if ( ( flags & PARAM_TYPE_MASK ) == REAL_PARAM && i < AUTO_RPC_FLOAT_REG_PARAMS && ( plen == sizeof( double ) || plen == sizeof( float ) ) ) { call.numRealParams++; call.realMap |= ( 1 << i ); } #endif // advance to next input arg. data += plen; } // space for lastParam? if ( &intCallParam[1] >= AUTO_RPC_ARRAY_END( call.intParams ) ) return 0; #pragma warning(disable:4311) // pointer truncation *( intCallParam++ ) = reinterpret_cast( lastParam ); #if AUTO_RPC_ABI == AUTO_RPC_ABI_SYSV_AMD64 // figure out how many args we have notched up. if ( memParam > &call.intParams[AUTO_RPC_INT_REG_PARAMS] && memParam > intCallParam ) intCallParam = memParam; #endif // convert from ptrdif_t to unsigned int; should be small enough, even if its 64-bit pointers. call.numIntParams = ( unsigned int )( intCallParam - call.intParams ); #if AUTO_RPC_FLOAT_REG_PARAMS && AUTO_RPC_ALLOC_SEPARATE_FLOATS call.numRealParams = ( unsigned int )( realCallParam - call.realParams ); #endif return 1; #else // AUTO_RPC_ABI return 0; #endif } // // @params // callParams: [IN] parameter list // functionPtr: [IN] function to call. // // @returns: // true: function was called. // false: too many parameters, probably. // bool GenRPC::CallWithStack( CallParams& call, void *functionPtr ) { #if AUTO_RPC_ABI // Are we x86-32? #if !defined( AUTO_RPC_NO_ASM ) && ( defined(__i386__) || defined( _M_IX86 ) || defined( __INTEL__ ) ) #if !defined(__GNUC__) // Intel dialect assembly NaturalWord const paramc = call.numIntParams; #pragma warning(disable:4311) // pointer truncation NaturalWord const paramv = reinterpret_cast( call.intParams ); _asm { // Load numbytes. mov ecx, paramc // allocate space on the stack for all these params lea edi,dword ptr[ecx * 4] sub esp,edi // setup source of copy mov esi, paramv // Setup the destination of the copy: the return stack. mov edi,esp // copy data rep movs dword ptr es:[edi],dword ptr [esi] // call the function call functionPtr // Restore the stack to its state, prior to our invocation. // // Detail: edi is one of the registers that must be preserved // across function calls. (The compiler should be saving it for us.) // // We left edi pointing to the end of the block copied; i.e. the state // of the stack prior to copying our params. So by loading it // into the esp we can restore the return stack to the state prior // to the copy. // mov esp,edi }; #else // GCC has its own form of asm block - so we'll always have to write two versions. // Therefore, as we're writing it twice, we use the ATT dialect, because only later // gcc support Intel dialect. This one also aligns the stack to a multiple of 16 bytes; which // windows doesn't seem to care about. // Be aware, we can't use offset of to get the address, as gcc insists on sticking. // NaturalWord const paramv = reinterpret_cast( call.intParams ); asm (\ "lea 4(%%ecx),%%esi\n\ mov (%%ecx),%%ecx\n\ lea (,%%ecx,4),%%edi\n\ sub %%edi,%%esp\n\ mov $12,%%edx\n\ and %%esp,%%edx\n\ sub %%edx,%%esp\n\ mov %%esp,%%edi\n\ rep movsl %%ds:(%%esi),%%es:(%%edi)\n\ add %%edx,%%edi\n\ call *%1\n\ mov %%edi,%%esp"\ : /* no outputs */\ : "c" ( &call ), "m" (functionPtr)\ : "%edi" , "%esi", "%edx", "%eax"\ ); #endif // GNUC vs non GNUC return 1; #elif !defined( AUTO_RPC_NO_ASM ) && ( defined( _M_X64 ) || defined( __x86_64__ ) || defined( _M_AMD64 ) ) #if AUTO_RPC_ABI == AUTO_RPC_ABI_WIN_AMD64 NaturalWord const paramv = reinterpret_cast( call.intParams ); _asm { // rcx := number of qwords to copy mov rcx, paramc // r9 := 0 sub r9,r9 // rsi => our parameter list. mov rsi, paramv // r9 := -(number of qwords to copy) sub r9,rcx // Preparation to align the stack to 16 byte boundary mov rdx,8 // rdi => projected bottom of stack lea rdi,dword ptr[rsp + r9 * 8] // figure out if stack needs aligning and rdx,rdi // save stack into rbx mov rbx,rsp // align stack sub rdi,rdx mov rsp,rdi // rdx => our parameter list mov rdx,rsi // // copy data - we copy all parameters, because we have to // create a shadow area; and this way its easiest. // rep movs qword ptr es:[edi],qword ptr [esi] // load registers // rcx|xmm0, rdx|xmm1,r8|xmm2,r9|xmm3 mov rcx,qword ptr [rdx] mov r8,qword ptr 16[rdx] movq xmm0,rcx mov r9,qword ptr 24[rdx] movq xmm2,r8 mov rdx,qword ptr 8[rdx] movq xmm3,r9 movq xmm1,rdx // call the function call functionPtr // Restore the stack to its state, prior to our invocation - // saved in rbx. mov rsp,rbx } #elif AUTO_RPC_ABI == AUTO_RPC_ABI_SYSV_AMD64 // // GCC won't generate a stack frame on higher optimization levels, so we don't touch it. // on -O3 it inlines the code, breaking it because of the jump reference. // // I figure a 64-bit compiler will be recent enough to do Intel syntax. May need to change // my mind on that. NB. Structure members are hard coded into this. // asm (\ ".intel_syntax noprefix\n\ push rbx\n\ mov rax,rsi\n\ push r15\n\ mov ecx,dword ptr[rdi+8+8*8]\n\ lea rsi,[rdi+8+8*8+8]\n\ mov r15,rsp\n\ lea rbx,[rdi+8]\n\ sub r8,r8\n\ sub rsp,8\n\ sub rcx,6\n\ lea r9,[rsi + 6 * 8]\n\ jbe .L1\n\ sub r8,rcx\n\ mov rdx,8\n\ lea rdi,qword ptr[rsp + r8 * 8]\n\ and rdx,rdi\n\ mov rsi,r9\n\ sub rdi,rdx\n\ mov rsp,rdi\n\ rep movsq \n\ .L1:\n\ movq xmm0,[rbx]\n\ movq xmm1,[rbx+8]\n\ movq xmm2,[rbx+16]\n\ movq xmm3,[rbx+24]\n\ movq xmm4,[rbx+32]\n\ movq xmm5,[rbx+40]\n\ movq xmm6,[rbx+48]\n\ movq xmm7,[rbx+56]\n\ mov rdi,[r9-48]\n\ mov rsi,[r9-40]\n\ mov rdx,[r9-32]\n\ mov rcx,[r9-24]\n\ mov r8,[r9-16]\n\ mov r9,[r9-8]\n\ call rax\n\ mov rsp,r15\n\ pop r15\n\ pop rbx\n\ .att_syntax prefix"\ : /* no outputs */\ : "D" ( &call ), "S" (functionPtr)\ : "%rdx", "%rcx" , "%r8", "%r9", "%rax",\ "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" ); // : "D", ( &call ), "c" ( &call.numIntParams ), "S" ( paramv ), "b" ( floatv ), "a" (functionPtr) #else #error unsupport ABI #endif return 1; #else // AUTO_RPC_NO_ASM or no x86-32/x86-64 // // 4. Passing the parameters. // // The compiler knows how to call functions, so having sorted out the argument list, // we just pass it to a function of the correct form - and let the compiler align stacks, // load registers, place parameters where they should be. // // This is particularly important as GCC has control over the stack frame, and it can // improperly frame it - for instance utilising red zones to save args, rather than pushing them. // On PowerPC it must create the parameter passing area, too. // // The most brute force way, is to code a function call for every possible number of parameters // // switch( paramc ) { // case 1: ( (void(AUTO_RPC_CALLSPEC*)(NaturalWord)) myfunc)( callParam[0] ); break; // case 2: ( (void(AUTO_RPC_CALLSPEC*)(NaturalWord,NaturalWord)) myfunc)( callParam[0], callParam[1] ); break; // ... // case 64: ( (void(AUTO_RPC_CALLSPEC*)(NaturalWord,NaturalWord)) myfunc)( callParam[0], callParam[1], ... , callParam[63] ); break; // } // // This is the only way to code WIN32 stdcall, for example, as the args must match exactly; // and so the only way to call from C if you need to call WINAPI routines. // // 2) Fortunately most calling conventions allowing excessive args. So this means we could // write something like below: // // ( (void(*)(...)) myfunc )( args[0],...,args[63] ); // // And although this should work, its a huge performance penalty copying between memory // locations for so many args. // // So we compromise - and do a stepped sequence. Noticing that the WIN64 ABI alwys requires // space for three args anyway. // // And on SysV x64 systems, the first 6 args are passed in reg; so again, these are only // copies into register, not memory copies. And finally that if we load word[n], word[n+1] // is loaded into the cache - thus the overhead for loading is not as big as it might be. // // For most realistic cases, a dozen args would be excessive. Obviously, if you have // a tested assembler equivalent, its probably better to use that. // // #if AUTO_RPC_FLOAT_REG_PARAMS if ( call.numRealParams == 0 ) #endif { if ( call.numIntParams <= 3 ) { ( (void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_3)) functionPtr )( AUTO_RPC_INT_ARGS_3( call ) ); return 1; } if ( call.numIntParams <= 6 ) { ( (void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_6)) functionPtr )( AUTO_RPC_INT_ARGS_6( call ) ); return 1; } if ( call.numIntParams <= 9 ) { ((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_9))functionPtr)( AUTO_RPC_INT_ARGS_9( call ) ); return 1; } if ( call.numIntParams <= 12 ) { ((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_12))functionPtr)( AUTO_RPC_INT_ARGS_12( call ) ); return 1; } if ( call.numIntParams <= 32 ) { ((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_32))functionPtr)( AUTO_RPC_INT_ARGS_32( call ) ); return 1; } if ( call.numIntParams <= 64 ) { ((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_64))functionPtr)( AUTO_RPC_INT_ARGS_64( call ) ); return 1; } } #if AUTO_RPC_FLOAT_REG_PARAMS && !AUTO_RPC_ALLOC_SEPARATE_FLOATS else { if ( call.numIntParams > 64 ) return 0; switch( call.realMap ) { // case 0: - no floats, never happens here. case 1: ( (void(AUTO_RPC_CALLSPEC*)(HardwareReal,NaturalWord,NaturalWord,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)( call.realParams[0], call.intParams[1], call.intParams[2], call.intParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 2: ((void(AUTO_RPC_CALLSPEC*)(NaturalWord,HardwareReal,NaturalWord,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)( call.intParams[0], call.realParams[1], call.intParams[2], call.intParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 3: ((void(AUTO_RPC_CALLSPEC*)(HardwareReal,HardwareReal,NaturalWord,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)( call.realParams[0], call.realParams[1], call.intParams[2], call.intParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 4: ( (void(AUTO_RPC_CALLSPEC*)(NaturalWord,NaturalWord,HardwareReal,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)( call.intParams[0], call.intParams[1], call.realParams[2], call.intParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 5: ((void(AUTO_RPC_CALLSPEC*)(HardwareReal,NaturalWord,HardwareReal,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)( call.realParams[0], call.intParams[1], call.realParams[2], call.intParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 6: ((void(AUTO_RPC_CALLSPEC*)(NaturalWord,HardwareReal,HardwareReal,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)( call.intParams[0], call.realParams[1], call.realParams[2], call.intParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 7: ((void(AUTO_RPC_CALLSPEC*)(HardwareReal,HardwareReal,HardwareReal,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)( call.realParams[0], call.realParams[1], call.realParams[2], call.intParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 8: ( (void(AUTO_RPC_CALLSPEC*)(NaturalWord,NaturalWord,NaturalWord,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)( call.intParams[0], call.intParams[1], call.intParams[2], call.realParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 9: ((void(AUTO_RPC_CALLSPEC*)(HardwareReal,NaturalWord,NaturalWord,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)( call.realParams[0], call.intParams[1], call.intParams[2], call.realParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 10: ((void(AUTO_RPC_CALLSPEC*)(NaturalWord,HardwareReal,NaturalWord,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)( call.intParams[0], call.realParams[1], call.intParams[2], call.realParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 11: ((void(AUTO_RPC_CALLSPEC*)(HardwareReal,HardwareReal,NaturalWord,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)( call.realParams[0], call.realParams[1], call.intParams[2], call.realParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 12: ( (void(AUTO_RPC_CALLSPEC*)(NaturalWord,NaturalWord,HardwareReal,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)( call.intParams[0], call.intParams[1], call.realParams[2], call.realParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 13: ((void(AUTO_RPC_CALLSPEC*)(HardwareReal,NaturalWord,HardwareReal,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)( call.realParams[0], call.intParams[1], call.realParams[2], call.realParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 14: ((void(AUTO_RPC_CALLSPEC*)(NaturalWord,HardwareReal,HardwareReal,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)( call.intParams[0], call.realParams[1], call.realParams[2], call.realParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; case 15: ((void(AUTO_RPC_CALLSPEC*)(HardwareReal,HardwareReal,HardwareReal,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)( call.realParams[0], call.realParams[1], call.realParams[2], call.realParams[3], AUTO_RPC_INT_ARGS_4_64( call ) ); break; default: return 0; } } #elif AUTO_RPC_FLOAT_REG_PARAMS else { // we pass FLOAT args last for powerpc compatibility. And although it means we pass them twice, // they should end up in the correct floating point register, with the rest of the integers in the // correct place... // // NB if you want to write inline asm for powerpc, you'll have to be put it in a separate // "naked" function to that uou can setup the parameter passing area and ensure its big enough. // (GCC will delete functions that are unused - it will delete the body of functions that // aren't called.) // if ( call.numIntParams <= 3 ) { ((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_3,AUTO_RPC_FLOAT_REG_TYPE))functionPtr)( AUTO_RPC_INT_ARGS_3( call ), AUTO_RPC_FLOAT_REG_ARGS( call ) ); return 1; } if ( call.numIntParams <= 6 ) { ((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_6,AUTO_RPC_FLOAT_REG_TYPE))functionPtr)( AUTO_RPC_INT_ARGS_6( call ),AUTO_RPC_FLOAT_REG_ARGS( call ) ); return 1; } if ( call.numIntParams <= 9 ) { ((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_9,AUTO_RPC_FLOAT_REG_TYPE))functionPtr)( AUTO_RPC_INT_ARGS_9( call ),AUTO_RPC_FLOAT_REG_ARGS( call ) ); return 1; } if ( call.numIntParams <= 12 ) { ( (void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_12,AUTO_RPC_FLOAT_REG_TYPE)) functionPtr )( AUTO_RPC_INT_ARGS_12( call ), AUTO_RPC_FLOAT_REG_ARGS( call ) ); return 1; } if ( call.numIntParams <= 64 ) { ( (void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_64,AUTO_RPC_FLOAT_REG_TYPE)) functionPtr )( AUTO_RPC_INT_ARGS_64( call ), AUTO_RPC_FLOAT_REG_ARGS( call ) ); return 1; } } #endif // AUTO_RPC_FLOAT_REG_PARAMS return 0; #endif // AUTO_RPC_NO_ASM #else // AUTO_RPC_ABI return 0; #endif } // --8<---8<----8<----8<---END