summaryrefslogtreecommitdiffstats
path: root/xpcom/reflect/xptcall/md/unix/xptcinvoke_ppc64_linux.cpp
blob: f7673bb29d36bb2c4b2c6ff8ee788a384bb72147 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// 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.
//
// 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.

// 7 integral parameters are passed in registers, not including |this|
// (i.e., r3-r10, with r3 being |this|).
const uint32_t GPR_COUNT = 7;

// 13 floating point parameters are passed in registers, either single or
// double precision (i.e., f1-f13).
const uint32_t FPR_COUNT = 13;

// 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)
{
    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())
            value = (uint64_t) &s->val;
        else {
            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 (nr_fpr < FPR_COUNT) {
                fpregs[nr_fpr++] = s->val.d;
                // Even if we have enough FPRs, still skip space in
                // the parameter area if we ran out of placeholder GPRs.
                if (nr_gpr < GPR_COUNT) {
                    nr_gpr++;
                } else {
                    d++;
                }
            } else {
                *((double *)d) = s->val.d;
                d++;
            }
        }
        else if (!s->IsIndirect() && s->type == nsXPTType::T_FLOAT) {
            if (nr_fpr < FPR_COUNT) {
                // Single-precision floats are passed in FPRs too.
                fpregs[nr_fpr++] = s->val.f;
                if (nr_gpr < GPR_COUNT) {
                    nr_gpr++;
                } else {
                    d++;
                }
            } else {
#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
                d++;
            }
        }
        else {
            if (nr_gpr < GPR_COUNT) {
                gpregs[nr_gpr++] = value;
            } else {
                *d++ = value;
            }
        }
    }
}

EXPORT_XPCOM_API(nsresult)
NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex, uint32_t paramCount,
                 nsXPTCVariant* params);