Bug 532198 - Reimplement xptcinvoke for arm/linux in C [r=Jacob.Bramley, r=vladimir, sr=benjamin]

--HG--
extra : rebase_source : 683a17a26c486d6f6bf376ff93fb8a0567a8ab47
This commit is contained in:
Mike Hommey 2010-04-19 22:55:45 +02:00
Родитель 8465a0be12
Коммит 66bec4216b
1 изменённых файлов: 72 добавлений и 143 удалений

Просмотреть файл

@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved. * the Initial Developer. All Rights Reserved.
* *
* Contributor(s): * Contributor(s):
* Mike Hommey <mh@glandium.org>
* *
* Alternatively, the contents of this file may be used under the terms of * Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"), * either of the GNU General Public License Version 2 or later (the "GPL"),
@ -43,84 +44,54 @@
#error "This code is for Linux ARM only. Check that it works on your system, too.\nBeware that this code is highly compiler dependent." #error "This code is for Linux ARM only. Check that it works on your system, too.\nBeware that this code is highly compiler dependent."
#endif #endif
/* This function copies a 64-bits word from dw to the given pointer in
* a buffer delimited by start and end, possibly wrapping around the
/* Note that we give a "worst case" estimate of how much stack _might_ be * buffer boundaries, and/or properly aligning the data at 64-bits word
* needed (for __ARM_EABI__), rather than the real count - this should be safe */ * boundaries (for EABI).
* start and end are both assumed to be 64-bits aligned.
#ifdef __ARM_EABI__ * Returns a pointer to the second 32-bits word copied (to accomodate
#define DOUBLEWORD_ALIGN(p) ((PRUint32 *)((((PRUint32)(p)) + 7) & 0xfffffff8)) * the invoke_copy_to_stack loop).
#define VAR_STACK_SIZE_64 3 */
#else static PRUint32 *
#define DOUBLEWORD_ALIGN(p) (p) copy_double_word(PRUint32 *start, PRUint32 *current, PRUint32 *end, PRUint64 *dw)
#define VAR_STACK_SIZE_64 2
#endif
// Remember that these 'words' are 32bit DWORDS
static PRUint32
invoke_count_words(PRUint32 paramCount, nsXPTCVariant* s)
{ {
PRUint32 result = 0;
for(PRUint32 i = 0; i < paramCount; i++, s++)
{
if(s->IsPtrData())
{
result++;
continue;
}
switch(s->type)
{
case nsXPTType::T_I8 :
case nsXPTType::T_I16 :
case nsXPTType::T_I32 :
result++;
break;
case nsXPTType::T_I64 :
result+=VAR_STACK_SIZE_64;
break;
case nsXPTType::T_U8 :
case nsXPTType::T_U16 :
case nsXPTType::T_U32 :
result++;
break;
case nsXPTType::T_U64 :
result+=VAR_STACK_SIZE_64;
break;
case nsXPTType::T_FLOAT :
result++;
break;
case nsXPTType::T_DOUBLE :
result+=VAR_STACK_SIZE_64;
break;
case nsXPTType::T_BOOL :
case nsXPTType::T_CHAR :
case nsXPTType::T_WCHAR :
result++;
break;
default:
// all the others are plain pointer types
result++;
break;
}
}
#ifdef __ARM_EABI__ #ifdef __ARM_EABI__
/* Ensure stack is always aligned to doubleword boundary; we take 3 words /* Aligning the pointer for EABI */
* off the stack to r1-r3 later, so it must always be on _odd_ word current = (PRUint32 *)(((PRUint32)current + 7) & ~7);
* boundary after this */ /* Wrap when reaching the end of the buffer */
if (result % 2 == 0) if (current == end) current = start;
result++; #else
/* On non-EABI, 64-bits values are not aligned and when we reach the end
* of the buffer, we need to write half of the data at the end, and the
* other half at the beginning. */
if (current == end - 1) {
*current = ((PRUint32*)dw)[0];
*start = ((PRUint32*)dw)[1];
return start;
}
#endif #endif
return result; *((PRUint64*) current) = *dw;
return current + 1;
} }
static void static void
invoke_copy_to_stack(PRUint32* d, PRUint32 paramCount, nsXPTCVariant* s) invoke_copy_to_stack(PRUint32* stk, PRUint32 *end,
PRUint32 paramCount, nsXPTCVariant* s)
{ {
/* The stack buffer is 64-bits aligned. The end argument points to its end.
* The caller is assumed to create a stack buffer of at least four 32-bits
* words.
* We use the last three 32-bit words to store the values for r1, r2 and r3
* for the method call, i.e. the first words for arguments passing.
*/
PRUint32 *d = end - 3;
for(PRUint32 i = 0; i < paramCount; i++, d++, s++) for(PRUint32 i = 0; i < paramCount; i++, d++, s++)
{ {
/* Wrap when reaching the end of the stack buffer */
if (d == end) d = stk;
NS_ASSERTION(d >= stk && d < end,
"invoke_copy_to_stack is copying outside its given buffer");
if(s->IsPtrData()) if(s->IsPtrData())
{ {
*((void**)d) = s->ptr; *((void**)d) = s->ptr;
@ -134,16 +105,19 @@ invoke_copy_to_stack(PRUint32* d, PRUint32 paramCount, nsXPTCVariant* s)
case nsXPTType::T_I8 : *((PRInt32*) d) = s->val.i8; break; case nsXPTType::T_I8 : *((PRInt32*) d) = s->val.i8; break;
case nsXPTType::T_I16 : *((PRInt32*) d) = s->val.i16; break; case nsXPTType::T_I16 : *((PRInt32*) d) = s->val.i16; break;
case nsXPTType::T_I32 : *((PRInt32*) d) = s->val.i32; break; case nsXPTType::T_I32 : *((PRInt32*) d) = s->val.i32; break;
case nsXPTType::T_I64 : d = DOUBLEWORD_ALIGN(d); case nsXPTType::T_I64 :
*((PRInt64*) d) = s->val.i64; d++; break; d = copy_double_word(stk, d, end, (PRUint64 *)&s->val.i64);
break;
case nsXPTType::T_U8 : *((PRUint32*)d) = s->val.u8; break; case nsXPTType::T_U8 : *((PRUint32*)d) = s->val.u8; break;
case nsXPTType::T_U16 : *((PRUint32*)d) = s->val.u16; break; case nsXPTType::T_U16 : *((PRUint32*)d) = s->val.u16; break;
case nsXPTType::T_U32 : *((PRUint32*)d) = s->val.u32; break; case nsXPTType::T_U32 : *((PRUint32*)d) = s->val.u32; break;
case nsXPTType::T_U64 : d = DOUBLEWORD_ALIGN(d); case nsXPTType::T_U64 :
*((PRUint64*)d) = s->val.u64; d++; break; d = copy_double_word(stk, d, end, (PRUint64 *)&s->val.u64);
break;
case nsXPTType::T_FLOAT : *((float*) d) = s->val.f; break; case nsXPTType::T_FLOAT : *((float*) d) = s->val.f; break;
case nsXPTType::T_DOUBLE : d = DOUBLEWORD_ALIGN(d); case nsXPTType::T_DOUBLE :
*((double*) d) = s->val.d; d++; break; d = copy_double_word(stk, d, end, (PRUint64 *)&s->val.d);
break;
case nsXPTType::T_BOOL : *((PRInt32*) d) = s->val.b; break; case nsXPTType::T_BOOL : *((PRInt32*) d) = s->val.b; break;
case nsXPTType::T_CHAR : *((PRInt32*) d) = s->val.c; break; case nsXPTType::T_CHAR : *((PRInt32*) d) = s->val.c; break;
case nsXPTType::T_WCHAR : *((PRInt32*) d) = s->val.wc; break; case nsXPTType::T_WCHAR : *((PRInt32*) d) = s->val.wc; break;
@ -155,48 +129,27 @@ invoke_copy_to_stack(PRUint32* d, PRUint32 paramCount, nsXPTCVariant* s)
} }
} }
extern "C" { typedef PRUint32 (*vtable_func)(nsISupports *, PRUint32, PRUint32, PRUint32);
struct my_params_struct {
nsISupports* that;
PRUint32 Index;
PRUint32 Count;
nsXPTCVariant* params;
PRUint32 fn_count;
PRUint32 fn_copy;
};
}
EXPORT_XPCOM_API(nsresult) EXPORT_XPCOM_API(nsresult)
NS_InvokeByIndex(nsISupports* that, PRUint32 methodIndex, NS_InvokeByIndex(nsISupports* that, PRUint32 methodIndex,
PRUint32 paramCount, nsXPTCVariant* params) PRUint32 paramCount, nsXPTCVariant* params)
{ {
PRUint32 result;
struct my_params_struct my_params;
my_params.that = that;
my_params.Index = methodIndex;
my_params.Count = paramCount;
my_params.params = params;
my_params.fn_copy = (PRUint32) &invoke_copy_to_stack;
my_params.fn_count = (PRUint32) &invoke_count_words;
/* This is to call a given method of class that. /* This is to call a given method of class that.
* The parameters are in params, the number is in paramCount. * The parameters are in params, the number is in paramCount.
* The routine will issue calls to count the number of words * The routine will issue calls to count the number of words
* required for argument passing and to copy the arguments to * required for argument passing and to copy the arguments to
* the stack. * the stack.
* Since APCS passes the first 3 params in r1-r3, we need to * ACPS passes the first 3 params in r1-r3 (with exceptions for 64-bits
* load the first three words from the stack and correct the stack * arguments), and the remaining goes onto the stack.
* pointer (sp) in the appropriate way. This means: * We allocate a buffer on the stack for a "worst case" estimate of how much
* * stack might be needed for EABI, i.e. twice the number of parameters.
* 1.) more than 3 arguments: load r1-r3, correct sp and remember No. * The end of this buffer will be used to store r1 to r3, so that the start
* of bytes left on the stack in r4 * of the stack is the remaining parameters.
* * The magic here is to call the method with "that" and three 32-bits
* 2.) <= 2 args: load r1-r3 (we won't be causing a stack overflow I hope), * arguments corresponding to r1-r3, so that the compiler generates the
* restore sp as if nothing had happened and set the marker r4 to zero. * proper function call. The stack will also contain the remaining arguments.
*
* Afterwards sp will be restored using the value in r4 (which is not a temporary register
* and will be preserved by the function/method called according to APCS [ARM Procedure
* Calling Standard]).
* *
* !!! IMPORTANT !!! * !!! IMPORTANT !!!
* This routine makes assumptions about the vtable layout of the c++ compiler. It's implemented * This routine makes assumptions about the vtable layout of the c++ compiler. It's implemented
@ -204,45 +157,21 @@ NS_InvokeByIndex(nsISupports* that, PRUint32 methodIndex,
* *
*/ */
__asm__ __volatile__( register vtable_func *vtable, func;
"ldr r1, [%1, #12] \n\t" /* prepare to call invoke_count_words */ register int base_size = (paramCount > 1) ? paramCount : 2;
"ldr ip, [%1, #16] \n\t" /* r0=paramCount, r1=params */ PRUint32 *stack_space = (PRUint32 *) __builtin_alloca(base_size * 8);
"ldr r0, [%1, #8] \n\t"
"mov lr, pc \n\t" /* call it... */ invoke_copy_to_stack(stack_space, &stack_space[base_size * 2],
"mov pc, ip \n\t" paramCount, params);
"mov r4, r0, lsl #2 \n\t" /* This is the amount of bytes needed. */
"sub sp, sp, r4 \n\t" /* use stack space for the args... */ vtable = *reinterpret_cast<vtable_func **>(that);
"mov r0, sp \n\t" /* prepare a pointer an the stack */
"ldr r1, [%1, #8] \n\t" /* =paramCount */
"ldr r2, [%1, #12] \n\t" /* =params */
"ldr ip, [%1, #20] \n\t" /* =invoke_copy_to_stack */
"mov lr, pc \n\t" /* copy args to the stack like the */
"mov pc, ip \n\t" /* compiler would. */
"ldr r0, [%1] \n\t" /* =that */
"ldr r1, [r0, #0] \n\t" /* get that->vtable offset */
"ldr r2, [%1, #4] \n\t"
"mov r2, r2, lsl #2 \n\t" /* a vtable_entry(x)=8 + (4 bytes * x) */
#if defined(__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100 /* G++ V3 ABI */ #if defined(__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100 /* G++ V3 ABI */
"ldr ip, [r1, r2] \n\t" /* get method adress from vtable */ func = vtable[methodIndex];
#else /* non G++ V3 ABI */ #else /* non G++ V3 ABI */
"add r2, r2, #8 \n\t" /* with this compilers */ func = vtable[2 + methodIndex];
"ldr ip, [r1, r2] \n\t" /* get method adress from vtable */
#endif #endif
"cmp r4, #12 \n\t" /* more than 3 arguments??? */
"ldmgtia sp!, {r1, r2, r3}\n\t" /* yes: load arguments for r1-r3 */ return func(that, stack_space[base_size * 2 - 3],
"subgt r4, r4, #12 \n\t" /* and correct the stack pointer */ stack_space[base_size * 2 - 2],
"ldmleia sp, {r1, r2, r3}\n\t" /* no: load r1-r3 from stack */ stack_space[base_size * 2 - 1]);
"addle sp, sp, r4 \n\t" /* and restore stack pointer */
"movle r4, #0 \n\t" /* a mark for restoring sp */
"ldr r0, [%1, #0] \n\t" /* get (self) */
"mov lr, pc \n\t" /* call mathod */
"mov pc, ip \n\t"
"add sp, sp, r4 \n\t" /* restore stack pointer */
"mov %0, r0 \n\t" /* the result... */
: "=r" (result)
: "r" (&my_params), "m" (my_params)
: "r0", "r1", "r2", "r3", "r4", "ip", "lr", "sp"
);
return result;
} }