1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 // Platform specific code to invoke XPCOM methods on native objects
7 
8 #include "xptcprivate.h"
9 
10 // The purpose of NS_InvokeByIndex() is to map a platform
11 // independent call to the platform ABI. To do that,
12 // NS_InvokeByIndex() has to determine the method to call via vtable
13 // access. The parameters for the method are read from the
14 // nsXPTCVariant* and prepared for the native ABI.
15 //
16 // Prior to POWER8, all 64-bit Power ISA systems used ELF v1 ABI, found
17 // here:
18 //   https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html
19 // and in particular:
20 //   https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUNC-CALL
21 // Little-endian ppc64le, however, uses ELF v2 ABI, which is here:
22 //   http://openpowerfoundation.org/wp-content/uploads/resources/leabi/leabi-20170510.pdf
23 // and in particular section 2.2, page 22. However, most big-endian ppc64
24 // systems still use ELF v1, so this file should support both.
25 
26 // 7 integral parameters are passed in registers, not including |this|
27 // (i.e., r3-r10, with r3 being |this|).
28 const uint32_t GPR_COUNT = 7;
29 
30 // 13 floating point parameters are passed in registers, either single or
31 // double precision (i.e., f1-f13).
32 const uint32_t FPR_COUNT = 13;
33 
34 // Both ABIs use the same register assignment strategy, as per this
35 // example from V1 ABI section 3.2.3 and V2 ABI section 2.2.3.2 [page 43]:
36 //
37 // typedef struct {
38 //   int    a;
39 //   double dd;
40 // } sparm;
41 // sparm   s, t;
42 // int     c, d, e;
43 // long double ld;
44 // double  ff, gg, hh;
45 //
46 // x = func(c, ff, d, ld, s, gg, t, e, hh);
47 //
48 // Parameter     Register     Offset in parameter save area
49 // c             r3           0-7    (not stored in parameter save area)
50 // ff            f1           8-15   (not stored)
51 // d             r5           16-23  (not stored)
52 // ld            f2,f3        24-39  (not stored)
53 // s             r8,r9        40-55  (not stored)
54 // gg            f4           56-63  (not stored)
55 // t             (none)       64-79  (stored in parameter save area)
56 // e             (none)       80-87  (stored)
57 // hh            f5           88-95  (not stored)
58 //
59 // i.e., each successive FPR usage skips a GPR, but not the other way around.
60 
invoke_copy_to_stack(uint64_t * gpregs,double * fpregs,uint32_t paramCount,nsXPTCVariant * s,uint64_t * d)61 extern "C" void invoke_copy_to_stack(uint64_t* gpregs, double* fpregs,
62                                      uint32_t paramCount, nsXPTCVariant* s,
63                                      uint64_t* d)
64 {
65     uint32_t nr_gpr = 0u;
66     uint32_t nr_fpr = 0u;
67     uint64_t value = 0u;
68 
69     for (uint32_t i = 0; i < paramCount; i++, s++) {
70         if (s->IsIndirect())
71             value = (uint64_t) &s->val;
72         else {
73             switch (s->type) {
74             case nsXPTType::T_FLOAT:                                break;
75             case nsXPTType::T_DOUBLE:                               break;
76             case nsXPTType::T_I8:     value = s->val.i8;            break;
77             case nsXPTType::T_I16:    value = s->val.i16;           break;
78             case nsXPTType::T_I32:    value = s->val.i32;           break;
79             case nsXPTType::T_I64:    value = s->val.i64;           break;
80             case nsXPTType::T_U8:     value = s->val.u8;            break;
81             case nsXPTType::T_U16:    value = s->val.u16;           break;
82             case nsXPTType::T_U32:    value = s->val.u32;           break;
83             case nsXPTType::T_U64:    value = s->val.u64;           break;
84             case nsXPTType::T_BOOL:   value = s->val.b;             break;
85             case nsXPTType::T_CHAR:   value = s->val.c;             break;
86             case nsXPTType::T_WCHAR:  value = s->val.wc;            break;
87             default:                  value = (uint64_t) s->val.p;  break;
88             }
89         }
90 
91         if (!s->IsIndirect() && s->type == nsXPTType::T_DOUBLE) {
92             if (nr_fpr < FPR_COUNT) {
93                 fpregs[nr_fpr++] = s->val.d;
94                 // Even if we have enough FPRs, still skip space in
95                 // the parameter area if we ran out of placeholder GPRs.
96                 if (nr_gpr < GPR_COUNT) {
97                     nr_gpr++;
98                 } else {
99                     d++;
100                 }
101             } else {
102                 *((double *)d) = s->val.d;
103                 d++;
104             }
105         }
106         else if (!s->IsIndirect() && s->type == nsXPTType::T_FLOAT) {
107             if (nr_fpr < FPR_COUNT) {
108                 // Single-precision floats are passed in FPRs too.
109                 fpregs[nr_fpr++] = s->val.f;
110                 if (nr_gpr < GPR_COUNT) {
111                     nr_gpr++;
112                 } else {
113                     d++;
114                 }
115             } else {
116 #ifdef __LITTLE_ENDIAN__
117                 *((float *)d) = s->val.f;
118 #else
119                 // Big endian needs adjustment to point to the least
120                 // significant word.
121                 float* p = (float*)d;
122                 p++;
123                 *p = s->val.f;
124 #endif
125                 d++;
126             }
127         }
128         else {
129             if (nr_gpr < GPR_COUNT) {
130                 gpregs[nr_gpr++] = value;
131             } else {
132                 *d++ = value;
133             }
134         }
135     }
136 }
137 
138 EXPORT_XPCOM_API(nsresult)
139 NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex, uint32_t paramCount,
140                  nsXPTCVariant* params);
141