1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_x86_64_unix.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,166 @@ 1.4 +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// Platform specific code to invoke XPCOM methods on native objects 1.11 + 1.12 +#include "xptcprivate.h" 1.13 + 1.14 +// 6 integral parameters are passed in registers 1.15 +const uint32_t GPR_COUNT = 6; 1.16 + 1.17 +// 8 floating point parameters are passed in SSE registers 1.18 +const uint32_t FPR_COUNT = 8; 1.19 + 1.20 +// Remember that these 'words' are 64-bit long 1.21 +static inline void 1.22 +invoke_count_words(uint32_t paramCount, nsXPTCVariant * s, 1.23 + uint32_t & nr_stack) 1.24 +{ 1.25 + uint32_t nr_gpr; 1.26 + uint32_t nr_fpr; 1.27 + nr_gpr = 1; // skip one GP register for 'that' 1.28 + nr_fpr = 0; 1.29 + nr_stack = 0; 1.30 + 1.31 + /* Compute number of eightbytes of class MEMORY. */ 1.32 + for (uint32_t i = 0; i < paramCount; i++, s++) { 1.33 + if (!s->IsPtrData() 1.34 + && (s->type == nsXPTType::T_FLOAT || s->type == nsXPTType::T_DOUBLE)) { 1.35 + if (nr_fpr < FPR_COUNT) 1.36 + nr_fpr++; 1.37 + else 1.38 + nr_stack++; 1.39 + } 1.40 + else { 1.41 + if (nr_gpr < GPR_COUNT) 1.42 + nr_gpr++; 1.43 + else 1.44 + nr_stack++; 1.45 + } 1.46 + } 1.47 +} 1.48 + 1.49 +static void 1.50 +invoke_copy_to_stack(uint64_t * d, uint32_t paramCount, nsXPTCVariant * s, 1.51 + uint64_t * gpregs, double * fpregs) 1.52 +{ 1.53 + uint32_t nr_gpr = 1; // skip one GP register for 'that' 1.54 + uint32_t nr_fpr = 0; 1.55 + uint64_t value; 1.56 + 1.57 + for (uint32_t i = 0; i < paramCount; i++, s++) { 1.58 + if (s->IsPtrData()) 1.59 + value = (uint64_t) s->ptr; 1.60 + else { 1.61 + switch (s->type) { 1.62 + case nsXPTType::T_FLOAT: break; 1.63 + case nsXPTType::T_DOUBLE: break; 1.64 + case nsXPTType::T_I8: value = s->val.i8; break; 1.65 + case nsXPTType::T_I16: value = s->val.i16; break; 1.66 + case nsXPTType::T_I32: value = s->val.i32; break; 1.67 + case nsXPTType::T_I64: value = s->val.i64; break; 1.68 + case nsXPTType::T_U8: value = s->val.u8; break; 1.69 + case nsXPTType::T_U16: value = s->val.u16; break; 1.70 + case nsXPTType::T_U32: value = s->val.u32; break; 1.71 + case nsXPTType::T_U64: value = s->val.u64; break; 1.72 + case nsXPTType::T_BOOL: value = s->val.b; break; 1.73 + case nsXPTType::T_CHAR: value = s->val.c; break; 1.74 + case nsXPTType::T_WCHAR: value = s->val.wc; break; 1.75 + default: value = (uint64_t) s->val.p; break; 1.76 + } 1.77 + } 1.78 + 1.79 + if (!s->IsPtrData() && s->type == nsXPTType::T_DOUBLE) { 1.80 + if (nr_fpr < FPR_COUNT) 1.81 + fpregs[nr_fpr++] = s->val.d; 1.82 + else { 1.83 + *((double *)d) = s->val.d; 1.84 + d++; 1.85 + } 1.86 + } 1.87 + else if (!s->IsPtrData() && s->type == nsXPTType::T_FLOAT) { 1.88 + if (nr_fpr < FPR_COUNT) 1.89 + // The value in %xmm register is already prepared to 1.90 + // be retrieved as a float. Therefore, we pass the 1.91 + // value verbatim, as a double without conversion. 1.92 + fpregs[nr_fpr++] = s->val.d; 1.93 + else { 1.94 + *((float *)d) = s->val.f; 1.95 + d++; 1.96 + } 1.97 + } 1.98 + else { 1.99 + if (nr_gpr < GPR_COUNT) 1.100 + gpregs[nr_gpr++] = value; 1.101 + else 1.102 + *d++ = value; 1.103 + } 1.104 + } 1.105 +} 1.106 + 1.107 +EXPORT_XPCOM_API(nsresult) 1.108 +NS_InvokeByIndex(nsISupports * that, uint32_t methodIndex, 1.109 + uint32_t paramCount, nsXPTCVariant * params) 1.110 +{ 1.111 + uint32_t nr_stack; 1.112 + invoke_count_words(paramCount, params, nr_stack); 1.113 + 1.114 + // Stack, if used, must be 16-bytes aligned 1.115 + if (nr_stack) 1.116 + nr_stack = (nr_stack + 1) & ~1; 1.117 + 1.118 + // Load parameters to stack, if necessary 1.119 + uint64_t *stack = (uint64_t *) __builtin_alloca(nr_stack * 8); 1.120 + uint64_t gpregs[GPR_COUNT]; 1.121 + double fpregs[FPR_COUNT]; 1.122 + invoke_copy_to_stack(stack, paramCount, params, gpregs, fpregs); 1.123 + 1.124 + // We used to have switches to make sure we would only load the registers 1.125 + // that are needed for this call. That produced larger code that was 1.126 + // not faster in practice. It also caused compiler warnings about the 1.127 + // variables being used uninitialized. 1.128 + // We now just load every every register. There could still be a warning 1.129 + // from a memory analysis tools that we are loading uninitialized stack 1.130 + // positions. 1.131 + 1.132 + // FIXME: this function depends on the above __builtin_alloca placing 1.133 + // the array in the correct spot for the ABI. 1.134 + 1.135 + // Load FPR registers from fpregs[] 1.136 + double d0, d1, d2, d3, d4, d5, d6, d7; 1.137 + 1.138 + d7 = fpregs[7]; 1.139 + d6 = fpregs[6]; 1.140 + d5 = fpregs[5]; 1.141 + d4 = fpregs[4]; 1.142 + d3 = fpregs[3]; 1.143 + d2 = fpregs[2]; 1.144 + d1 = fpregs[1]; 1.145 + d0 = fpregs[0]; 1.146 + 1.147 + // Load GPR registers from gpregs[] 1.148 + uint64_t a0, a1, a2, a3, a4, a5; 1.149 + 1.150 + a5 = gpregs[5]; 1.151 + a4 = gpregs[4]; 1.152 + a3 = gpregs[3]; 1.153 + a2 = gpregs[2]; 1.154 + a1 = gpregs[1]; 1.155 + a0 = (uint64_t) that; 1.156 + 1.157 + // Get pointer to method 1.158 + uint64_t methodAddress = *((uint64_t *)that); 1.159 + methodAddress += 8 * methodIndex; 1.160 + methodAddress = *((uint64_t *)methodAddress); 1.161 + 1.162 + typedef nsresult (*Method)(uint64_t, uint64_t, uint64_t, uint64_t, 1.163 + uint64_t, uint64_t, double, double, double, 1.164 + double, double, double, double, double); 1.165 + nsresult result = ((Method)methodAddress)(a0, a1, a2, a3, a4, a5, 1.166 + d0, d1, d2, d3, d4, d5, 1.167 + d6, d7); 1.168 + return result; 1.169 +}