From: Mike Hommey Date: Fri, 30 Apr 2010 11:03:50 +0200 Subject: Add xptcall support for SH4 processors Closes: #553593 https://bugzilla.mozilla.org/show_bug.cgi?id=382214 --- xpcom/reflect/xptcall/md/unix/moz.build | 5 + .../src/md/unix/xptcinvoke_linux_sh.cpp | 203 +++++++++++++ .../src/md/unix/xptcstubs_linux_sh.cpp | 271 ++++++++++++++++++ 3 files changed, 479 insertions(+) create mode 100644 xpcom/reflect/xptcall/src/md/unix/xptcinvoke_linux_sh.cpp create mode 100644 xpcom/reflect/xptcall/src/md/unix/xptcstubs_linux_sh.cpp diff --git a/xpcom/reflect/xptcall/md/unix/moz.build b/xpcom/reflect/xptcall/md/unix/moz.build index 1779c14..43a10a2 100644 --- a/xpcom/reflect/xptcall/md/unix/moz.build +++ b/xpcom/reflect/xptcall/md/unix/moz.build @@ -262,6 +262,11 @@ if CONFIG["OS_ARCH"] == "Linux": CXXFLAGS += [ "-fno-integrated-as", ] + elif CONFIG['OS_TEST'] in ('sh4', 'sh4a'): + SOURCES += [ + 'xptcinvoke_linux_sh.cpp', + 'xptcstubs_linux_sh.cpp', + ] if CONFIG["OS_ARCH"] == "Linux" and CONFIG["CPU_ARCH"] == "riscv64": SOURCES += [ diff --git a/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_linux_sh.cpp b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_linux_sh.cpp new file mode 100644 index 0000000..ca4807d --- /dev/null +++ b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_linux_sh.cpp @@ -0,0 +1,203 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * - Copyright (C) 2008-2009 STMicroelectronics + * + * 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"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * Based on the neutroni port, however that assumed that the compiler was pushing params + * onto the stack. Change to take this into account. + * + * ***** END LICENSE BLOCK ***** */ + +#include "xptcprivate.h" + +extern "C" { + +const int c_int_register_params = 4; +const int c_float_register_params = 8; + +static PRUint32 __attribute__((__used__)) +copy_to_stack(PRUint32 **that,PRUint32 methodIndex,PRUint32 paramCount, nsXPTCVariant* s,PRUint32* data) +{ + int intCount = 1; // Because of that + int floatCount = 0; + PRUint32 *intRegParams=data+1 ; + float *floatRegParams = (float *)(data+4); + + /* Push the that register into the right place so it can be restored on exit */ + *data= (PRUint32)(that); + data+=12; /* 4 integer registers, and 8 floating point registers */ + + 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; + } + } + + /* Now calculate the return address + * Dereference that to get vtable pointer + */ + return *( (*(that))+(methodIndex) ); + +} + +} + + /* This was originally done as a C function, but the original code was + * relying on how the compiler laid out the stack. Later versions of + * gcc do a better job of optimising and never push the parameters on the + * stack. So it is simpler to just write the whole thing in assembler anyway + */ + + /* 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 bottom of which will be copied to registers r4 to r7 + and fr4 to fr11 before calling the target function. + */ + +/* r4= that, r5=methodIndex,r6=paramCount,r7=params */ + + __asm__ ( + + + /* Make space for parameters to be passed to the method. Assume worst case + 8 bytes per parameter. Also leave space for 4 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. + */ + ".section .text\n" + ".balign 4\n" + ".global NS_InvokeByIndex_P\n" + "NS_InvokeByIndex_P:\n" + "mov.l r14, @-r15 \n\t" // Push frame + "sts.l pr, @-r15 \n\t" // Push link + "mov.l r8, @-r15 \n\t" // Save + "mov r15, r14\n\t" // Set frame + "mov #3, r1 \n\t" // Assume worse case, all params are 64bit, hence *8 + "mov r6, r2\n\t" + "shld r1, r2 \n\t" + "mov r2, r8 \n\t" // Save stack drop + "add #48, r2 \n\t" // Space for 4 longs, 8 floats + "sub r2, r15 \n\t" // Drop stack + "mov.l 1f, r1 \n\t" // Get address of copy_to_stack_function + "jsr @r1 \n\t" + "mov.l r15, @-r15 \n\t" // Params will be dumped here + "add #4, r15 \n\t" // Pop stack ptr param. r0 contains method address + + /* 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.l @r15+, r4 \n\t" // that + "mov.l @r15+, r5 \n\t" + "mov.l @r15+, r6 \n\t" + "mov.l @r15+, r7 \n\t" + "fmov.s @r15+, fr5 \n\t" + "fmov.s @r15+, fr4 \n\t" + "fmov.s @r15+, fr7 \n\t" + "fmov.s @r15+, fr6 \n\t" + "fmov.s @r15+, fr9 \n\t" + "fmov.s @r15+, fr8 \n\t" + "fmov.s @r15+, fr11 \n\t" + "jsr @r0 \n\t" // Invoke method + "fmov.s @r15+, fr10 \n\t" + "add r8, r15\n\t" // Pop stack back + "mov.l @r15+, r8\n\t" // Restore r8 + "lds.l @r15+, pr\n\t" + "rts\n\t" + "mov.l @r15+, r14\n\t" + ".balign 4\n\t" + "1: .long copy_to_stack \n\t" + ); + diff --git a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_linux_sh.cpp b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_linux_sh.cpp new file mode 100644 index 0000000..cf9fcea --- /dev/null +++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_linux_sh.cpp @@ -0,0 +1,271 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * - Copyright (C) 2008-2009 STMicroelectronics + * + * 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"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * Based on the neutrino code, with some bug fixes and using the C preprocessor + * like all the other ports rather than the python script. + * + * ***** END LICENSE BLOCK ***** */ + +#include "xptcprivate.h" +#include "xptiprivate.h" + +const int c_int_register_params = 3; +const int c_float_register_params = 8; + +/* + Dispatch function for all stubs. + + The parameters to the original function are spread between 'data' which + is value of the stack pointer when the stub was called, intRegParams which + points to an area containing the values of r5, r6 and r7 when the stub was + called and floatRegParams which points to an area containing the values + of float registers fr4 to fr11 when the stub was called. + + */ +extern "C" nsresult +PrepareAndDispatch(nsXPTCStubBase* self, int methodIndex, PRUint32* data, + PRUint32 *intRegParams, float *floatRegParams) +{ +#define PARAM_BUFFER_COUNT 16 + + nsresult result = NS_ERROR_FAILURE; + int intCount = 0; + int floatCount = 0; + nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT]; + nsXPTCMiniVariant* dispatchParams = NULL; + const nsXPTMethodInfo* info; + PRUint8 paramCount; + PRUint8 i; + + NS_ASSERTION(self,"no self"); + + self->mEntry->GetMethodInfo(PRUint16(methodIndex), &info); + NS_ASSERTION(info,"no interface info"); + + paramCount = info->GetParamCount(); + + // setup variant array pointer + if(paramCount > PARAM_BUFFER_COUNT) + dispatchParams = new nsXPTCMiniVariant[paramCount]; + else + dispatchParams = paramBuffer; + NS_ASSERTION(dispatchParams,"no place for params"); + + for ( i = 0; i < paramCount; ++i ) { + const nsXPTParamInfo& param = info->GetParam(i); + nsXPTCMiniVariant* dp = &dispatchParams[i]; + nsXPTType type = param.IsOut() ? nsXPTType::T_I32 : param.GetType(); + + switch ( type ) { + case nsXPTType::T_I64: + case nsXPTType::T_U64: + // Was this passed in a register? + if ( (c_int_register_params - intCount) >= 2 ) { + dp->val.i64 = *((PRInt64 *) intRegParams); + intRegParams += 2; + intCount += 2; + } + else { + dp->val.i64 = *((PRInt64*) data); + data += 2; + } + break; + case nsXPTType::T_FLOAT: + // Was this passed in a register? + if ( floatCount < c_float_register_params ) { + dp->val.f = *floatRegParams; + ++floatCount; + ++floatRegParams; + } + else { + dp->val.f = *((float*) data); + ++data; + } + break; + case nsXPTType::T_DOUBLE: + // Was this passed in a register? + if ( (c_float_register_params - floatCount) >= 2 ) { + if ( floatCount & 1 != 0 ) { + ++floatCount; + ++floatRegParams; + } + dp->val.d = *(double *)floatRegParams; + floatCount += 2; + floatRegParams += 2; + } + else { + dp->val.d = *((double *) data); + data += 2; + } + break; + default: // 32-bit (non-float) value + // Was this passed in a register? + if ( intCount < c_int_register_params ) { + dp->val.i32 = *intRegParams; + ++intRegParams; + ++intCount; + } + else { + dp->val.i32 = *data; + ++data; + } + break; + } + } + + result = self->mOuter->CallMethod((PRUint16)methodIndex, info, dispatchParams); + + if(dispatchParams != paramBuffer) + delete [] dispatchParams; + + return result; +} + + +__asm__ ( + ".text\n" + ".little\n" + ".section .rodata\n" + + ".globl SharedStub\n" + ".type SharedStub, @function\n" + "SharedStub:\n" + "mov r15, r1\n" + "mov.l r14,@-r15\n" + "sts.l pr,@-r15\n" + "mov r15, r14\n" + + /* Some parameters might have been passed in registers, so push them + * all onto the stack, PrepareAndDispatch can then work out whats what + * given method type information. + */ + "mov.l r7, @-r15\n" + "mov.l r6, @-r15\n" + "mov.l r5, @-r15\n" + "mov r15, r7\n" /* r7 = PrepareAndDispatch intRegParams param */ + + "fmov.s fr10, @-r15\n" + "fmov.s fr11, @-r15\n" + "fmov.s fr8, @-r15\n" + "fmov.s fr9, @-r15\n" + "fmov.s fr6, @-r15\n" + "fmov.s fr7, @-r15\n" + "fmov.s fr4, @-r15\n" + "fmov.s fr5, @-r15\n" + "mov.l r15, @-r15\n" /* PrepareAndDispatch floatRegParams param */ + + "mov r1, r6\n" /* r6 = PrepareAndDispatch data param */ + + "mov.l 1f, r1\n" + "jsr @r1\n" /* Note, following instruction is executed first*/ + "mov r2, r5\n" /* r5 = PrepareAndDispatch methodIndex param */ + + "mov r14,r15\n" + "lds.l @r15+,pr\n" + "mov.l @r15+,r14\n" + "rts\n" + "nop\n" + ".align 2\n" + "1:\n" + ".long PrepareAndDispatch\n" + ); + +#define STUB_ENTRY(n) \ +__asm__( \ + ".text\n" \ + ".align 1 \n" \ + ".if " #n " < 10\n\t" \ + ".globl _ZN14nsXPTCStubBase5Stub" #n "Ev\n\t" \ + ".type _ZN14nsXPTCStubBase5Stub" #n "Ev,@function\n"\ + "_ZN14nsXPTCStubBase5Stub" #n "Ev:\n\t" \ + ".elseif " #n " < 100\n\t" \ + ".globl _ZN14nsXPTCStubBase6Stub" #n "Ev\n\t" \ + ".type _ZN14nsXPTCStubBase6Stub" #n "Ev,@function\n"\ + "_ZN14nsXPTCStubBase6Stub" #n "Ev:\n\t" \ + ".elseif " #n " < 1000\n\t" \ + ".globl _ZN14nsXPTCStubBase7Stub" #n "Ev\n\t" \ + ".type _ZN14nsXPTCStubBase7Stub" #n "Ev,@function\n"\ + "_ZN14nsXPTCStubBase7Stub" #n "Ev:\n\t" \ + ".else\n\t" \ + ".err \"stub number " #n " >= 1000 not yet supported\"\n\t" \ + ".endif\n\t" \ + "mov.l 1f, r1 \n" \ + ".if "#n" < 128 \n" \ + "jmp @r1 \n" \ + " mov #"#n",r2 \n" \ + ".elseif "#n" < 256 \n" \ + "mov #"#n", r2 \n" \ + "jmp @r1 \n" \ + " extu.b r2, r2 \n" \ + ".else \n" \ + "mov #"#n" & 0xff,r2 \n" \ + "extu.b r2, r2 \n" \ + "mov #"#n">>8, r3 \n" \ + "shll8 r3 \n" \ + "jmp @r1 \n" \ + " or r3, r2 \n" \ + ".endif \n" \ + ".if "#n" % 20 == 0\n" \ + ".align 2\n" \ + "1:\n" \ + ".long SharedStub\n" \ + ".endif\n" \ + ); + + +/* Due to the fact that the SH4 can only load forward labels, we have + * to use sentinel_entry to output the last label. A better solution + * would be to introduce a STUB_LAST macro in the defs.in file, but + * this will do for now + */ + +#define SENTINEL_ENTRY(n) \ +__asm__( \ +".if "#n" == 0 \n" \ + ".text \n" \ + ".align 2\n" \ + "1:\n" \ + ".long SharedStub\n" \ +".endif\n" \ +); \ + \ +nsresult nsXPTCStubBase::Sentinel##n() \ +{ \ + NS_ASSERTION(0,"nsXPTCStubBase::Sentinel called"); \ + return NS_ERROR_NOT_IMPLEMENTED; \ +} + +#include "xptcstubsdef.inc"