зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
8465a0be12
Коммит
66bec4216b
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче