#include "xptcprivate.h" extern "C" { const int c_int_register_params = 3; const int c_float_register_params = 8; static void copy_to_stack(PRUint32 paramCount, nsXPTCVariant* s, int size, PRUint32* data) { int intCount = 0; int floatCount = 0; PRUint32 *intRegParams = data + (size / sizeof(PRUint32)) - c_int_register_params; float *floatRegParams = (float *)intRegParams - c_float_register_params; for ( PRUint32 i = 0; i < paramCount; ++i, ++s ) { nsXPTType type = s->IsPtrData() ? nsXPTType::T_I32 : s->type; switch ( type ) { case nsXPTType::T_I64: case nsXPTType::T_U64: // Space to pass in registers? if ( (c_int_register_params - intCount) >= 2 ) { *((PRInt64 *) intRegParams) = s->val.i64; intRegParams += 2; intCount += 2; } else { *((PRInt64*) data) = s->val.i64; data += 2; } break; case nsXPTType::T_FLOAT: // Space to pass in registers? if ( floatCount < c_float_register_params ) { *floatRegParams = s->val.f; ++floatCount; ++floatRegParams; } else { *((float*) data) = s->val.f; ++data; } break; case nsXPTType::T_DOUBLE: // Space to pass in registers? if ( (c_float_register_params - floatCount) >= 2 ) { if ( (floatCount & 1) != 0 ) { ++floatCount; ++floatRegParams; } *(double *)floatRegParams = s->val.d; floatCount += 2; floatRegParams += 2; } else { *((double *) data) = s->val.d; data += 2; } break; default: // 32 (non-float) value PRInt32 value = (PRInt32) (s->IsPtrData() ? s->ptr : s->val.p); // Space to pass in registers? if ( intCount < c_int_register_params ) { *intRegParams = value; ++intRegParams; ++intCount; } else { *data = value; ++data; } break; } } } XPTC_PUBLIC_API(nsresult) XPTC_InvokeByIndex(nsISupports* that, PRUint32 methodIndex, PRUint32 paramCount, nsXPTCVariant* params) { #ifdef __GNUC__ /* Gnu compiler. */ int result; void (*fn_copy) (PRUint32 paramCount, nsXPTCVariant* s, int size, PRUint32* d) = copy_to_stack; /* Because the SH processor passes the first few parameters in registers it is a bit tricky setting things up right. To make things easier, all the hard work will be done by copy_to_stack above. We pass to it a chunk of memory, the top of which will be copied to registers r5 to r7 and fr4 to fr11 before calling the target function. */ __asm__ __volatile__( /* Make space for parameters to be passed to the method. Assume worst case 8 bytes per parameter. Also leave space for 3 longs and 8 floats that will be put into registers. The worst case is all int64 parameters and even in this case 8 bytes are passed in registers so we can deduct this from our allocation. */ "mov.l @(8,r14), r4 \n\t" // copy_to_stack paramCount parameter "mov r4, r6 \n\t" "mov #3, r1 \n\t" "shld r1, r6 \n\t" "add #36, r6 \n\t" // Space for 3 longs, 8 floats - 8 bytes "sub r6, r15 \n\t" "mov r15, r7 \n\t" "mov.l @(20,r14), r1 \n\t" // fn_copy "jsr @r1 \n\t" // Note, next instruction is executed first "mov.l @(12,r14), r5 \n\t" // copy_to_stack data parameter /* Now everything is laid out nicely in the stack. We just have to load values at the top end of the memory area into registers and make the call. We may load more things into registers than needed, but nobody will care about that. */ "mov r14, r1 \n\t" "add #-44, r1 \n\t" "fmov.s @r1+, fr5 \n\t" "fmov.s @r1+, fr4 \n\t" "fmov.s @r1+, fr7 \n\t" "fmov.s @r1+, fr6 \n\t" "fmov.s @r1+, fr9 \n\t" "fmov.s @r1+, fr8 \n\t" "fmov.s @r1+, fr11 \n\t" "fmov.s @r1+, fr10 \n\t" "mov.l @r1+, r5 \n\t" "mov.l @r1+, r6 \n\t" "mov.l @r1+, r7 \n\t" "mov.l @r14, r1 \n\t" // that -> r1 "mov.l @r1, r1 \n\t" // vtbl -> r1 "mov.l @(4,r14), r4 \n\t" // methodIndex -> r4 "shll2 r4 \n\t" "add r4, r1 \n\t" "mov.l @r1, r1 \n\t" // method addr -> r1 "jsr @r1 \n\t" "mov.l @r14, r4 \n\t" // that -> r4 (note, executed before jsr) "mov.l r0, @(16,r14) \n\r" // Save return value in result : : : "r1", "r4", "r6", "r7", "pr", "dr4", "dr6", "dr8" ); return result; #else #error "can't find a compiler to use" #endif /* __GNUC__ */ } }