From 5d23e29da99dbe841060182c8ce09cc388285ed9 Mon Sep 17 00:00:00 2001 From: "jband%netscape.com" Date: Sun, 23 Apr 2000 21:14:11 +0000 Subject: [PATCH] checking in changes from Stefan Hanske (who wrote the original version of the Linux ARM code) --- xpcom/reflect/xptcall/src/md/unix/Makefile.in | 1 + .../xptcall/src/md/unix/xptcinvoke_arm.cpp | 81 ++++++++++++------- .../xptcall/src/md/unix/xptcstubs_arm.cpp | 53 +++++++++--- 3 files changed, 94 insertions(+), 41 deletions(-) diff --git a/xpcom/reflect/xptcall/src/md/unix/Makefile.in b/xpcom/reflect/xptcall/src/md/unix/Makefile.in index ed03e8dcc61a..5b7b907aa8e6 100644 --- a/xpcom/reflect/xptcall/src/md/unix/Makefile.in +++ b/xpcom/reflect/xptcall/src/md/unix/Makefile.in @@ -93,6 +93,7 @@ endif ifeq ($(OS_ARCH),Linux) ifneq (,$(filter arm32 armv4l sa110,$(OS_TEST))) CPPSRCS := xptcinvoke_arm.cpp xptcstubs_arm.cpp +OS_CXXFLAGS += -O2 endif endif # diff --git a/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_arm.cpp b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_arm.cpp index 0cb5f2c41111..646f5b506902 100644 --- a/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_arm.cpp +++ b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_arm.cpp @@ -24,8 +24,8 @@ #include "xptcprivate.h" -#if !defined(LINUX) || !defined(__arm) -#error "this code is for Linux ARM only" +#if !defined(LINUX) || !defined(__arm__) +#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 // Remember that these 'words' are 32bit DWORDS @@ -135,37 +135,62 @@ XPTC_InvokeByIndex(nsISupports* that, PRUint32 methodIndex, 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. + * The parameters are in params, the number is in paramCount. + * The routine will issue calls to count the number of words + * required for argument passing and to copy the arguments to + * the stack. + * Since APCS passes the first 3 params in r1-r3, we need to + * load the first three words from the stack and correct the stack + * pointer (sp) in the appropriate way. This means: + * + * 1.) more than 3 arguments: load r1-r3, correct sp and remember No. + * of bytes left on the stack in r4 + * + * 2.) <= 2 args: load r1-r3 (we won't be causing a stack overflow I hope), + * restore sp as if nothing had happened and set the marker r4 to zero. + * + * 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 !!! + * This routine makes assumptions about the vtable layout of the c++ compiler. It's implemented + * for arm-linux GNU g++ >= 2.8.1 (including egcs and gcc-2.95.[1-3])! + * + */ + __asm__ __volatile__( - "ldr r1, [%1, #12] \n\t" - "ldr ip, [%1, #16] \n\t" + "ldr r1, [%1, #12] \n\t" /* prepare to call invoke_count_words */ + "ldr ip, [%1, #16] \n\t" /* r0=paramCount, r1=params */ "ldr r0, [%1, #8] \n\t" - "mov lr, pc \n\t" + "mov lr, pc \n\t" /* call it... */ "mov pc, ip \n\t" - "mov r4, r0, lsl #2 \n\t" - "sub sp, sp, r4 \n\t" - "mov r0, sp \n\t" - "ldr r1, [%1, #8] \n\t" - "ldr r2, [%1, #12] \n\t" - "ldr ip, [%1, #20] \n\t" - "mov lr, pc \n\t" - "mov pc, ip \n\t" - "ldr r0, [%1] \n\t" - "ldr r1, [r0, #0] \n\t" + "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... */ + "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" - "add r2, r2, #8 \n\t" - "ldr ip, [r1, r2] \n\t" - "cmp r4, #12 \n\t" - "ldmgtia sp!, {r1, r2, r3}\n\t" - "subgt r4, r4, #12 \n\t" - "ldmleia sp, {r1, r2, r3}\n\t" - "addle sp, sp, r4 \n\t" - "movle r4, #0 \n\t" - "ldr r0, [%1, #0] \n\t" - "mov lr, pc \n\t" + "mov r2, r2, lsl #2 \n\t" /* a vtable_entry(x)=8 + (4 bytes * x) */ + "add r2, r2, #8 \n\t" /* with this compilers */ + "ldr ip, [r1, r2] \n\t" /* get method adress from vtable */ + "cmp r4, #12 \n\t" /* more than 3 arguments??? */ + "ldmgtia sp!, {r1, r2, r3}\n\t" /* yes: load arguments for r1-r3 */ + "subgt r4, r4, #12 \n\t" /* and correct the stack pointer */ + "ldmleia sp, {r1, r2, r3}\n\t" /* no: load r1-r3 from stack */ + "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" - "mov %0, r0 \n\t" + "add sp, sp, r4 \n\t" /* restore stack pointer */ + "mov %0, r0 \n\t" /* the result... */ : "=r" (result) : "r" (&my_params) : "r0", "r1", "r2", "r3", "r4", "ip", "lr" diff --git a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_arm.cpp b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_arm.cpp index a3912b6a9ac5..fbe8207e1743 100644 --- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_arm.cpp +++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_arm.cpp @@ -24,8 +24,8 @@ #include "xptcprivate.h" -#if !defined(LINUX) || !defined(__arm) -#error "this code is for Linux ARM only" +#if !defined(LINUX) || !defined(__arm__) +#error "This code is for Linux ARM only. Please check if it works for you, too.\nDepends strongly on gcc behaviour." #endif static nsresult @@ -102,37 +102,64 @@ PrepareAndDispatch(nsXPTCStubBase* self, uint32 methodIndex, PRUint32* args) return result; } + +/* The easiest way to implement this is to do it as a c++ method. This means that + * the compiler will same some registers to the stack as soon as this method is + * entered. We have to be aware of that and have to know the number of registers + * that will be pushed to the stack for now. + * The compiler passes arguments to functions/methods in r1-r3 and the rest is on the + * stack. r0 is (self). + * + * + * !!! IMPORTANT !!! + * This code will *not* work if compiled without optimization (-O / -O2) because + * the compiler overwrites the registers r0-r3 (it thinks it's called without + * parameters) and perhaps reserves more stack space than we think. + * + * + * Since we don't know the number of parameters we have to pass to the required + * method we use the following scheme: + * 1.) Save method parameters that are passed in r1-r3 to a secure location + * 2.) copy the stack space that is reserved at method entry to a secure location + * 3.) copy the method arguments that formerly where in r1-r3 right in front of the + * other arguments (if any). PrepareAndDispatch needs all arguments in an array. + * It will sort out the correct argument passing convention using the InterfaceInfo's. + * 4.) Call PrepareAndDispatch + * 5.) Copy the stack contents from our method entry back in place to exit cleanly. + * + * The easier way would be to completly implement this in assembler. This way one could get rid + * of the compiler generated function prologue. + #define STUB_ENTRY(n) \ nsresult nsXPTCStubBase::Stub##n() \ { \ - register void* method = &PrepareAndDispatch; \ register nsresult result; \ __asm__ __volatile__( \ "sub sp, sp, #32 \n\t" /* correct stack for pushing all args */ \ "str r1, [sp] \n\t" /* push all args in r1-r3 to stack */ \ - "str r2, [sp, #4] \n\t" /* to make a PRUint32 array of */ \ - "str r3, [sp, #8] \n\t" /* functin parameters */ \ + "str r2, [sp, #4] \n\t" /* */ \ + "str r3, [sp, #8] \n\t" /* */ \ "add r1, sp, #32 \n\t" /* copy saved registers: */ \ "ldr r2, [r1] \n\t" /* The scene is as follows - */ \ "str r2, [sp, #12] \n\t" /* sl, fp, ip, lr, pc get saved to the */ \ "ldr r2, [r1, # 4] \n\t" /* stack, and behind that is the rest */ \ "str r2, [sp, #16] \n\t" /* of our function parameters. */ \ - "ldr r2, [r1, # 8] \n\t" /* So we make some room and copy r1-r3 */ \ - "str r2, [sp, #20] \n\t" /* right in place. */ \ + "ldr r2, [r1, # 8] \n\t" /* */ \ + "str r2, [sp, #20] \n\t" /* */ \ "ldr r2, [r1, #12] \n\t" \ "str r2, [sp, #24] \n\t" \ "ldr r2, [r1, #16] \n\t" \ "str r2, [sp, #28] \n\t" \ - "ldmia sp, {r1, r2, r3}\n\t" \ - "add lr, sp, #40 \n\t" \ + "ldmia sp, {r1, r2, r3}\n\t" /* Copy method arguments to the right */ \ + "add lr, sp, #40 \n\t" /* location. */ \ "stmia lr, {r1, r2, r3}\n\t" \ "add sp, sp, #12 \n\t" \ - "mov r1, #"#n" \n\t" /* methodIndex */ \ - "mov r2, lr \n\t" /* &args */ \ + "mov r1, #"#n" \n\t" /* = methodIndex */ \ + "mov r2, lr \n\t" /* = &(args) */ \ "bl PrepareAndDispatch__FP14nsXPTCStubBaseUiPUi \n\t" /*PrepareAndDispatch*/ \ "mov %0, r0 \n\t" /* Result */ \ - "add r0, sp, #20 \n\t" \ - "ldmia sp!, {r1,r2,r3} \n\t" \ + "add r0, sp, #20 \n\t" /* copy everything back in place for */ \ + "ldmia sp!, {r1,r2,r3} \n\t" /* the normal c++ m,ethod exit */ \ "stmia r0!, {r1,r2,r3} \n\t" \ "ldmia sp!, {r1, r2} \n\t" \ "stmia r0, {r1, r2} \n\t" \