# HG changeset patch # Parent d4ba07ab572268a34e98c63792beb2ce6d78e7a1 This patch is upstream diff -r d4ba07ab5722 js/xpconnect/src/XPCWrappedNative.cpp --- a/js/xpconnect/src/XPCWrappedNative.cpp Fri Sep 20 07:56:01 2019 +0200 +++ b/js/xpconnect/src/XPCWrappedNative.cpp Fri Sep 20 08:00:19 2019 +0200 @@ -1157,10 +1157,6 @@ return helper.get().Call(); } -#if (__GNUC__ && __linux__ && __PPC64__ && _LITTLE_ENDIAN) -// Work around a compiler bug on ppc64le (bug 1512162). -__attribute__ ((noinline,noclone)) -#endif bool CallMethodHelper::Call() { mCallContext.SetRetVal(JS::UndefinedValue()); @@ -1319,10 +1315,6 @@ return true; } -#if (__GNUC__ && __linux__ && __PPC64__ && _LITTLE_ENDIAN) -// Work around a compiler bug on ppc64le (bug 1512162). -__attribute__ ((noinline,noclone)) -#endif bool CallMethodHelper::GatherAndConvertResults() { // now we iterate through the native params to gather and convert results uint8_t paramCount = mMethodInfo->GetParamCount(); diff -r d4ba07ab5722 xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc64_linux.S --- a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc64_linux.S Fri Sep 20 07:56:01 2019 +0200 +++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_ppc64_linux.S Fri Sep 20 08:00:19 2019 +0200 @@ -151,10 +151,10 @@ ld r2,STACK_TOC(r1) # Load our own TOC pointer ld r1,0(r1) # Revert stack frame ld 0,16(r1) # Reload lr + mtlr 0 ld 29,-24(r1) # Restore NVGPRS ld 30,-16(r1) ld 31,-8(r1) - mtlr 0 blr #if _CALL_ELF == 2 diff -r d4ba07ab5722 xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc64_linux.cpp --- a/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc64_linux.cpp Fri Sep 20 07:56:01 2019 +0200 +++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc64_linux.cpp Fri Sep 20 08:00:19 2019 +0200 @@ -5,93 +5,126 @@ // Platform specific code to invoke XPCOM methods on native objects +#include "xptcprivate.h" + // The purpose of NS_InvokeByIndex() is to map a platform // independent call to the platform ABI. To do that, // NS_InvokeByIndex() has to determine the method to call via vtable // access. The parameters for the method are read from the // nsXPTCVariant* and prepared for the native ABI. - -// The PowerPC64 platform ABI can be found here: -// http://www.freestandards.org/spec/ELF/ppc64/ +// +// Prior to POWER8, all 64-bit Power ISA systems used ELF v1 ABI, found +// here: +// https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html // and in particular: -// http://www.freestandards.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#FUNC-CALL - -#include -#include "xptcprivate.h" +// https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUNC-CALL +// Little-endian ppc64le, however, uses ELF v2 ABI, which is here: +// http://openpowerfoundation.org/wp-content/uploads/resources/leabi/leabi-20170510.pdf +// and in particular section 2.2, page 22. However, most big-endian ppc64 +// systems still use ELF v1, so this file should support both. -// 8 integral parameters are passed in registers, not including 'that' -#define GPR_COUNT 7 +// 7 integral parameters are passed in registers, not including |this| +// (i.e., r3-r10, with r3 being |this|). +const uint32_t GPR_COUNT = 7; -// 8 floating point parameters are passed in registers, floats are -// promoted to doubles when passed in registers -#define FPR_COUNT 13 - -extern "C" uint32_t -invoke_count_words(uint32_t paramCount, nsXPTCVariant* s) -{ - return uint32_t(((paramCount * 2) + 3) & ~3); -} +// 13 floating point parameters are passed in registers, either single or +// double precision (i.e., f1-f13). +const uint32_t FPR_COUNT = 13; -extern "C" void -invoke_copy_to_stack(uint64_t* gpregs, - double* fpregs, - uint32_t paramCount, - nsXPTCVariant* s, - uint64_t* d) +// Both ABIs use the same register assignment strategy, as per this +// example from V1 ABI section 3.2.3 and V2 ABI section 2.2.3.2 [page 43]: +// +// typedef struct { +// int a; +// double dd; +// } sparm; +// sparm s, t; +// int c, d, e; +// long double ld; +// double ff, gg, hh; +// +// x = func(c, ff, d, ld, s, gg, t, e, hh); +// +// Parameter Register Offset in parameter save area +// c r3 0-7 (not stored in parameter save area) +// ff f1 8-15 (not stored) +// d r5 16-23 (not stored) +// ld f2,f3 24-39 (not stored) +// s r8,r9 40-55 (not stored) +// gg f4 56-63 (not stored) +// t (none) 64-79 (stored in parameter save area) +// e (none) 80-87 (stored) +// hh f5 88-95 (not stored) +// +// i.e., each successive FPR usage skips a GPR, but not the other way around. + +extern "C" void invoke_copy_to_stack(uint64_t* gpregs, double* fpregs, + uint32_t paramCount, nsXPTCVariant* s, + uint64_t* d) { - uint64_t tempu64; + uint32_t nr_gpr = 0u; + uint32_t nr_fpr = 0u; + uint64_t value = 0u; - for(uint32_t i = 0; i < paramCount; i++, s++) { - if(s->IsIndirect()) - tempu64 = (uint64_t) &s->val; + for (uint32_t i = 0; i < paramCount; i++, s++) { + if (s->IsIndirect()) + value = (uint64_t) &s->val; else { - switch(s->type) { - case nsXPTType::T_FLOAT: break; - case nsXPTType::T_DOUBLE: break; - case nsXPTType::T_I8: tempu64 = s->val.i8; break; - case nsXPTType::T_I16: tempu64 = s->val.i16; break; - case nsXPTType::T_I32: tempu64 = s->val.i32; break; - case nsXPTType::T_I64: tempu64 = s->val.i64; break; - case nsXPTType::T_U8: tempu64 = s->val.u8; break; - case nsXPTType::T_U16: tempu64 = s->val.u16; break; - case nsXPTType::T_U32: tempu64 = s->val.u32; break; - case nsXPTType::T_U64: tempu64 = s->val.u64; break; - case nsXPTType::T_BOOL: tempu64 = s->val.b; break; - case nsXPTType::T_CHAR: tempu64 = s->val.c; break; - case nsXPTType::T_WCHAR: tempu64 = s->val.wc; break; - default: tempu64 = (uint64_t) s->val.p; break; + switch (s->type) { + case nsXPTType::T_FLOAT: break; + case nsXPTType::T_DOUBLE: break; + case nsXPTType::T_I8: value = s->val.i8; break; + case nsXPTType::T_I16: value = s->val.i16; break; + case nsXPTType::T_I32: value = s->val.i32; break; + case nsXPTType::T_I64: value = s->val.i64; break; + case nsXPTType::T_U8: value = s->val.u8; break; + case nsXPTType::T_U16: value = s->val.u16; break; + case nsXPTType::T_U32: value = s->val.u32; break; + case nsXPTType::T_U64: value = s->val.u64; break; + case nsXPTType::T_BOOL: value = s->val.b; break; + case nsXPTType::T_CHAR: value = s->val.c; break; + case nsXPTType::T_WCHAR: value = s->val.wc; break; + default: value = (uint64_t) s->val.p; break; } } if (!s->IsIndirect() && s->type == nsXPTType::T_DOUBLE) { - if (i < FPR_COUNT) - fpregs[i] = s->val.d; - else - *(double *)d = s->val.d; + if (nr_fpr < FPR_COUNT) { + fpregs[nr_fpr++] = s->val.d; + nr_gpr++; + } else { + *((double *)d) = s->val.d; + d++; + } } else if (!s->IsIndirect() && s->type == nsXPTType::T_FLOAT) { - if (i < FPR_COUNT) { - fpregs[i] = s->val.f; // if passed in registers, floats are promoted to doubles + if (nr_fpr < FPR_COUNT) { + // Single-precision floats are passed in FPRs too. + fpregs[nr_fpr++] = s->val.f; + nr_gpr++; } else { - float *p = (float *)d; -#ifndef __LITTLE_ENDIAN__ +#ifdef __LITTLE_ENDIAN__ + *((float *)d) = s->val.f; +#else + // Big endian needs adjustment to point to the least + // significant word. + float* p = (float*)d; p++; + *p = s->val.f; #endif - *p = s->val.f; + d++; } } else { - if (i < GPR_COUNT) - gpregs[i] = tempu64; - else - *d = tempu64; + if (nr_gpr < GPR_COUNT) { + gpregs[nr_gpr++] = value; + } else { + *d++ = value; + } } - if (i >= 7) - d++; } } EXPORT_XPCOM_API(nsresult) -NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex, - uint32_t paramCount, nsXPTCVariant* params); - +NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex, uint32_t paramCount, + nsXPTCVariant* params); diff -r d4ba07ab5722 xpcom/reflect/xptcall/md/unix/xptcstubs_ppc64_linux.cpp --- a/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc64_linux.cpp Fri Sep 20 07:56:01 2019 +0200 +++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_ppc64_linux.cpp Fri Sep 20 08:00:19 2019 +0200 @@ -7,36 +7,64 @@ #include "xptcprivate.h" -// The Linux/PPC64 ABI passes the first 8 integral -// parameters and the first 13 floating point parameters in registers -// (r3-r10 and f1-f13), no stack space is allocated for these by the -// caller. The rest of the parameters are passed in the caller's stack -// area. The stack pointer has to retain 16-byte alignment. +// Prior to POWER8, all 64-bit Power ISA systems used ELF v1 ABI, found +// here: +// https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html +// and in particular: +// https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUNC-CALL +// Little-endian ppc64le, however, uses ELF v2 ABI, which is here: +// http://openpowerfoundation.org/wp-content/uploads/resources/leabi/leabi-20170510.pdf +// and in particular section 2.2, page 22. However, most big-endian ppc64 +// systems still use ELF v1, so this file should support both. +// +// Both ABIs pass the first 8 integral parameters and the first 13 floating +// point parameters in registers r3-r10 and f1-f13. No stack space is +// allocated for these by the caller. The rest of the parameters are passed +// in the caller's stack area. The stack pointer must stay 16-byte aligned. -// The PowerPC64 platform ABI can be found here: -// http://www.freestandards.org/spec/ELF/ppc64/ -// and in particular: -// http://www.freestandards.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#FUNC-CALL - -#define PARAM_BUFFER_COUNT 16 -#define GPR_COUNT 7 -#define FPR_COUNT 13 +const uint32_t PARAM_BUFFER_COUNT = 16; +const uint32_t GPR_COUNT = 7; +const uint32_t FPR_COUNT = 13; // PrepareAndDispatch() is called by SharedStub() and calls the actual method. // // - 'args[]' contains the arguments passed on stack -// - 'gprData[]' contains the arguments passed in integer registers -// - 'fprData[]' contains the arguments passed in floating point registers +// - 'gpregs[]' contains the arguments passed in integer registers +// - 'fpregs[]' contains the arguments passed in floating point registers // // The parameters are mapped into an array of type 'nsXPTCMiniVariant' // and then the method gets called. -#include +// +// Both ABIs use the same register assignment strategy, as per this +// example from V1 ABI section 3.2.3 and V2 ABI section 2.2.3.2 [page 43]: +// +// typedef struct { +// int a; +// double dd; +// } sparm; +// sparm s, t; +// int c, d, e; +// long double ld; +// double ff, gg, hh; +// +// x = func(c, ff, d, ld, s, gg, t, e, hh); +// +// Parameter Register Offset in parameter save area +// c r3 0-7 (not stored in parameter save area) +// ff f1 8-15 (not stored) +// d r5 16-23 (not stored) +// ld f2,f3 24-39 (not stored) +// s r8,r9 40-55 (not stored) +// gg f4 56-63 (not stored) +// t (none) 64-79 (stored in parameter save area) +// e (none) 80-87 (stored) +// hh f5 88-95 (not stored) +// +// i.e., each successive FPR usage skips a GPR, but not the other way around. + extern "C" nsresult ATTRIBUTE_USED -PrepareAndDispatch(nsXPTCStubBase* self, - uint64_t methodIndex, - uint64_t* args, - uint64_t *gprData, - double *fprData) +PrepareAndDispatch(nsXPTCStubBase * self, uint32_t methodIndex, + uint64_t * args, uint64_t * gpregs, double *fpregs) { nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT]; nsXPTCMiniVariant* dispatchParams = nullptr; @@ -48,7 +76,7 @@ self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info); NS_ASSERTION(info,"no method info"); - if (! info) + if (!info) return NS_ERROR_UNEXPECTED; paramCount = info->GetParamCount(); @@ -66,9 +94,11 @@ const uint8_t indexOfJSContext = info->IndexOfJSContext(); uint64_t* ap = args; - uint32_t iCount = 0; - uint32_t fpCount = 0; - uint64_t tempu64; + // |that| is implicit in the calling convention; we really do start at the + // first GPR (as opposed to x86_64). + uint32_t nr_gpr = 0; + uint32_t nr_fpr = 0; + uint64_t value; for(i = 0; i < paramCount; i++) { const nsXPTParamInfo& param = info->GetParam(i); @@ -76,67 +106,67 @@ nsXPTCMiniVariant* dp = &dispatchParams[i]; if (i == indexOfJSContext) { - if (iCount < GPR_COUNT) - iCount++; + if (nr_gpr < GPR_COUNT) + nr_gpr++; else ap++; } if (!param.IsOut() && type == nsXPTType::T_DOUBLE) { - if (fpCount < FPR_COUNT) { - dp->val.d = fprData[fpCount++]; - } - else - dp->val.d = *(double*) ap; - } else if (!param.IsOut() && type == nsXPTType::T_FLOAT) { - if (fpCount < FPR_COUNT) { - dp->val.f = (float) fprData[fpCount++]; // in registers floats are passed as doubles + if (nr_fpr < FPR_COUNT) { + dp->val.d = fpregs[nr_fpr++]; + nr_gpr++; + } else { + dp->val.d = *(double*)ap++; } - else { - float *p = (float *)ap; -#ifndef __LITTLE_ENDIAN__ + continue; + } + if (!param.IsOut() && type == nsXPTType::T_FLOAT) { + if (nr_fpr < FPR_COUNT) { + // Single-precision floats are passed in FPRs too. + dp->val.f = (float)fpregs[nr_fpr++]; + nr_gpr++; + } else { +#ifdef __LITTLE_ENDIAN__ + dp->val.f = *(float*)ap++; +#else + // Big endian needs adjustment to point to the least + // significant word. + float* p = (float*)ap; p++; -#endif dp->val.f = *p; + ap++; +#endif } - } else { /* integer type or pointer */ - if (iCount < GPR_COUNT) - tempu64 = gprData[iCount]; - else - tempu64 = *ap; + continue; + } + if (nr_gpr < GPR_COUNT) + value = gpregs[nr_gpr++]; + else + value = *ap++; - if (param.IsOut() || !type.IsArithmetic()) - dp->val.p = (void*) tempu64; - else if (type == nsXPTType::T_I8) - dp->val.i8 = (int8_t) tempu64; - else if (type == nsXPTType::T_I16) - dp->val.i16 = (int16_t) tempu64; - else if (type == nsXPTType::T_I32) - dp->val.i32 = (int32_t) tempu64; - else if (type == nsXPTType::T_I64) - dp->val.i64 = (int64_t) tempu64; - else if (type == nsXPTType::T_U8) - dp->val.u8 = (uint8_t) tempu64; - else if (type == nsXPTType::T_U16) - dp->val.u16 = (uint16_t) tempu64; - else if (type == nsXPTType::T_U32) - dp->val.u32 = (uint32_t) tempu64; - else if (type == nsXPTType::T_U64) - dp->val.u64 = (uint64_t) tempu64; - else if (type == nsXPTType::T_BOOL) - dp->val.b = (bool) tempu64; - else if (type == nsXPTType::T_CHAR) - dp->val.c = (char) tempu64; - else if (type == nsXPTType::T_WCHAR) - dp->val.wc = (wchar_t) tempu64; - else - NS_ERROR("bad type"); + if (param.IsOut() || !type.IsArithmetic()) { + dp->val.p = (void*) value; + continue; } - if (iCount < GPR_COUNT) - iCount++; // gprs are skipped for fp args, so this always needs inc - else - ap++; + switch (type) { + case nsXPTType::T_I8: dp->val.i8 = (int8_t) value; break; + case nsXPTType::T_I16: dp->val.i16 = (int16_t) value; break; + case nsXPTType::T_I32: dp->val.i32 = (int32_t) value; break; + case nsXPTType::T_I64: dp->val.i64 = (int64_t) value; break; + case nsXPTType::T_U8: dp->val.u8 = (uint8_t) value; break; + case nsXPTType::T_U16: dp->val.u16 = (uint16_t) value; break; + case nsXPTType::T_U32: dp->val.u32 = (uint32_t) value; break; + case nsXPTType::T_U64: dp->val.u64 = (uint64_t) value; break; + case nsXPTType::T_BOOL: dp->val.b = (bool) value; break; + case nsXPTType::T_CHAR: dp->val.c = (char) value; break; + case nsXPTType::T_WCHAR: dp->val.wc = (wchar_t) value; break; + + default: + NS_ERROR("bad type"); + break; + } } nsresult result = self->mOuter->CallMethod((uint16_t) methodIndex, info, @@ -150,23 +180,19 @@ // Load r11 with the constant 'n' and branch to SharedStub(). // +// As G++3 ABI contains the length of the functionname in the mangled +// name, it is difficult to get a generic assembler mechanism like +// in the G++ 2.95 case. // XXX Yes, it's ugly that we're relying on gcc's name-mangling here; // however, it's quick, dirty, and'll break when the ABI changes on // us, which is what we want ;-). - - -// gcc-3 version -// -// As G++3 ABI contains the length of the functionname in the mangled -// name, it is difficult to get a generic assembler mechanism like -// in the G++ 2.95 case. // Create names would be like: // _ZN14nsXPTCStubBase5Stub1Ev // _ZN14nsXPTCStubBase6Stub12Ev // _ZN14nsXPTCStubBase7Stub123Ev // _ZN14nsXPTCStubBase8Stub1234Ev // etc. -// Use assembler directives to get the names right... +// Use assembler directives to get the names right. #if _CALL_ELF == 2 # define STUB_ENTRY(n) \ @@ -252,7 +278,7 @@ #define SENTINEL_ENTRY(n) \ nsresult nsXPTCStubBase::Sentinel##n() \ { \ - NS_ERROR("nsXPTCStubBase::Sentinel called"); \ + NS_ERROR("nsXPTCStubBase::Sentinel called"); \ return NS_ERROR_NOT_IMPLEMENTED; \ }