|
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 // Platform specific code to invoke XPCOM methods on native objects |
|
8 |
|
9 #include "xptcprivate.h" |
|
10 |
|
11 // 6 integral parameters are passed in registers |
|
12 const uint32_t GPR_COUNT = 6; |
|
13 |
|
14 // 8 floating point parameters are passed in SSE registers |
|
15 const uint32_t FPR_COUNT = 8; |
|
16 |
|
17 // Remember that these 'words' are 64-bit long |
|
18 static inline void |
|
19 invoke_count_words(uint32_t paramCount, nsXPTCVariant * s, |
|
20 uint32_t & nr_stack) |
|
21 { |
|
22 uint32_t nr_gpr; |
|
23 uint32_t nr_fpr; |
|
24 nr_gpr = 1; // skip one GP register for 'that' |
|
25 nr_fpr = 0; |
|
26 nr_stack = 0; |
|
27 |
|
28 /* Compute number of eightbytes of class MEMORY. */ |
|
29 for (uint32_t i = 0; i < paramCount; i++, s++) { |
|
30 if (!s->IsPtrData() |
|
31 && (s->type == nsXPTType::T_FLOAT || s->type == nsXPTType::T_DOUBLE)) { |
|
32 if (nr_fpr < FPR_COUNT) |
|
33 nr_fpr++; |
|
34 else |
|
35 nr_stack++; |
|
36 } |
|
37 else { |
|
38 if (nr_gpr < GPR_COUNT) |
|
39 nr_gpr++; |
|
40 else |
|
41 nr_stack++; |
|
42 } |
|
43 } |
|
44 } |
|
45 |
|
46 static void |
|
47 invoke_copy_to_stack(uint64_t * d, uint32_t paramCount, nsXPTCVariant * s, |
|
48 uint64_t * gpregs, double * fpregs) |
|
49 { |
|
50 uint32_t nr_gpr = 1; // skip one GP register for 'that' |
|
51 uint32_t nr_fpr = 0; |
|
52 uint64_t value; |
|
53 |
|
54 for (uint32_t i = 0; i < paramCount; i++, s++) { |
|
55 if (s->IsPtrData()) |
|
56 value = (uint64_t) s->ptr; |
|
57 else { |
|
58 switch (s->type) { |
|
59 case nsXPTType::T_FLOAT: break; |
|
60 case nsXPTType::T_DOUBLE: break; |
|
61 case nsXPTType::T_I8: value = s->val.i8; break; |
|
62 case nsXPTType::T_I16: value = s->val.i16; break; |
|
63 case nsXPTType::T_I32: value = s->val.i32; break; |
|
64 case nsXPTType::T_I64: value = s->val.i64; break; |
|
65 case nsXPTType::T_U8: value = s->val.u8; break; |
|
66 case nsXPTType::T_U16: value = s->val.u16; break; |
|
67 case nsXPTType::T_U32: value = s->val.u32; break; |
|
68 case nsXPTType::T_U64: value = s->val.u64; break; |
|
69 case nsXPTType::T_BOOL: value = s->val.b; break; |
|
70 case nsXPTType::T_CHAR: value = s->val.c; break; |
|
71 case nsXPTType::T_WCHAR: value = s->val.wc; break; |
|
72 default: value = (uint64_t) s->val.p; break; |
|
73 } |
|
74 } |
|
75 |
|
76 if (!s->IsPtrData() && s->type == nsXPTType::T_DOUBLE) { |
|
77 if (nr_fpr < FPR_COUNT) |
|
78 fpregs[nr_fpr++] = s->val.d; |
|
79 else { |
|
80 *((double *)d) = s->val.d; |
|
81 d++; |
|
82 } |
|
83 } |
|
84 else if (!s->IsPtrData() && s->type == nsXPTType::T_FLOAT) { |
|
85 if (nr_fpr < FPR_COUNT) |
|
86 // The value in %xmm register is already prepared to |
|
87 // be retrieved as a float. Therefore, we pass the |
|
88 // value verbatim, as a double without conversion. |
|
89 fpregs[nr_fpr++] = s->val.d; |
|
90 else { |
|
91 *((float *)d) = s->val.f; |
|
92 d++; |
|
93 } |
|
94 } |
|
95 else { |
|
96 if (nr_gpr < GPR_COUNT) |
|
97 gpregs[nr_gpr++] = value; |
|
98 else |
|
99 *d++ = value; |
|
100 } |
|
101 } |
|
102 } |
|
103 |
|
104 EXPORT_XPCOM_API(nsresult) |
|
105 NS_InvokeByIndex(nsISupports * that, uint32_t methodIndex, |
|
106 uint32_t paramCount, nsXPTCVariant * params) |
|
107 { |
|
108 uint32_t nr_stack; |
|
109 invoke_count_words(paramCount, params, nr_stack); |
|
110 |
|
111 // Stack, if used, must be 16-bytes aligned |
|
112 if (nr_stack) |
|
113 nr_stack = (nr_stack + 1) & ~1; |
|
114 |
|
115 // Load parameters to stack, if necessary |
|
116 uint64_t *stack = (uint64_t *) __builtin_alloca(nr_stack * 8); |
|
117 uint64_t gpregs[GPR_COUNT]; |
|
118 double fpregs[FPR_COUNT]; |
|
119 invoke_copy_to_stack(stack, paramCount, params, gpregs, fpregs); |
|
120 |
|
121 // We used to have switches to make sure we would only load the registers |
|
122 // that are needed for this call. That produced larger code that was |
|
123 // not faster in practice. It also caused compiler warnings about the |
|
124 // variables being used uninitialized. |
|
125 // We now just load every every register. There could still be a warning |
|
126 // from a memory analysis tools that we are loading uninitialized stack |
|
127 // positions. |
|
128 |
|
129 // FIXME: this function depends on the above __builtin_alloca placing |
|
130 // the array in the correct spot for the ABI. |
|
131 |
|
132 // Load FPR registers from fpregs[] |
|
133 double d0, d1, d2, d3, d4, d5, d6, d7; |
|
134 |
|
135 d7 = fpregs[7]; |
|
136 d6 = fpregs[6]; |
|
137 d5 = fpregs[5]; |
|
138 d4 = fpregs[4]; |
|
139 d3 = fpregs[3]; |
|
140 d2 = fpregs[2]; |
|
141 d1 = fpregs[1]; |
|
142 d0 = fpregs[0]; |
|
143 |
|
144 // Load GPR registers from gpregs[] |
|
145 uint64_t a0, a1, a2, a3, a4, a5; |
|
146 |
|
147 a5 = gpregs[5]; |
|
148 a4 = gpregs[4]; |
|
149 a3 = gpregs[3]; |
|
150 a2 = gpregs[2]; |
|
151 a1 = gpregs[1]; |
|
152 a0 = (uint64_t) that; |
|
153 |
|
154 // Get pointer to method |
|
155 uint64_t methodAddress = *((uint64_t *)that); |
|
156 methodAddress += 8 * methodIndex; |
|
157 methodAddress = *((uint64_t *)methodAddress); |
|
158 |
|
159 typedef nsresult (*Method)(uint64_t, uint64_t, uint64_t, uint64_t, |
|
160 uint64_t, uint64_t, double, double, double, |
|
161 double, double, double, double, double); |
|
162 nsresult result = ((Method)methodAddress)(a0, a1, a2, a3, a4, a5, |
|
163 d0, d1, d2, d3, d4, d5, |
|
164 d6, d7); |
|
165 return result; |
|
166 } |