summaryrefslogtreecommitdiffstats
path: root/xpcom/reflect/xptcall/md/unix/xptcinvoke_aarch64.cpp
blob: 385cba17ad38e7ba883eb39b545cd453215c4a84 (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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/* -*- 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"

#if !defined(__aarch64__)
#error "This code is for Linux AArch64 only."
#endif


/* "Procedure Call Standard for the ARM 64-bit Architecture" document, sections
 * "5.4 Parameter Passing" and "6.1.2 Procedure Calling" contain all the
 * needed information.
 *
 * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042d/IHI0042D_aapcs.pdf
 */

#ifndef __AARCH64EL__
#error "Only little endian compatibility was tested"
#endif

// The AAPCS doesn't require argument widening, but Apple's calling convention
// does. If we are really fortunate, the compiler will clean up all the
// copying for us.
template<typename T>
inline uint64_t normalize_arg(T value) {
    return (uint64_t)value;
}

template<>
inline uint64_t normalize_arg(float value) {
    uint64_t result = 0;
    memcpy(&result, &value, sizeof(value));
    return result;
}

template<>
inline uint64_t normalize_arg(double value) {
    uint64_t result = 0;
    memcpy(&result, &value, sizeof(value));
    return result;
}

/*
 * Allocation of function arguments to their appropriate place in registers
 * if possible and then to the stack.  Handling of 'that' argument which
 * goes to register r0 is handled separately and does not belong here.
 *
 * Note that we are handling integer arguments and floating-point arguments
 * identically, depending on which register area is passed to this function.
 *
 * 'reg_args'     - pointer to the current position in the buffer,
 *                  corresponding to the register arguments.
 * 'reg_args_end' - pointer to the end of the registers argument
 *                  buffer.
 * 'stack_args'   - pointer to the current position in the buffer,
 *                  corresponding to the arguments on stack.
 * 'data'         - typed data to put on the stack.
 */
template<typename T>
static inline void alloc_arg(uint64_t* &reg_args,
                             uint64_t* reg_args_end,
                             void* &stack_args,
                             T*     data)
{
    if (reg_args < reg_args_end) {
        *reg_args = normalize_arg(*data);
        reg_args++;
    } else {
        // According to the ABI, types that are smaller than 8 bytes are
        // passed in registers or 8-byte stack slots.  This rule is only
        // partially true on Apple platforms, where types smaller than 8
        // bytes occupy only the space they require on the stack and
        // their stack slot must be properly aligned.
#ifdef __APPLE__
        const size_t aligned_size = sizeof(T);
#else
        const size_t aligned_size = 8;
#endif
        // Ensure the pointer is aligned for the type
        uintptr_t addr = (reinterpret_cast<uintptr_t>(stack_args) + aligned_size - 1) & ~(aligned_size - 1);
        memcpy(reinterpret_cast<void*>(addr), data, sizeof(T));
        // Point the stack to the next slot.
        stack_args = reinterpret_cast<void*>(addr + aligned_size);
    }
}

extern "C" void
invoke_copy_to_stack(uint64_t* stk, uint64_t *end,
                     uint32_t paramCount, nsXPTCVariant* s)
{
    uint64_t* ireg_args = stk;
    uint64_t* ireg_end  = ireg_args + 8;
    // Pun on integer and floating-point registers being the same size.
    uint64_t* freg_args = ireg_end;
    uint64_t* freg_end  = freg_args + 8;
    void* stack_args = freg_end;

    // leave room for 'that' argument in x0
    ++ireg_args;

    for (uint32_t i = 0; i < paramCount; i++, s++) {
        if (s->IsIndirect()) {
            void* ptr = &s->val;
            alloc_arg(ireg_args, ireg_end, stack_args, &ptr);
        } else {
            switch (s->type) {
                case nsXPTType::T_FLOAT:
                    alloc_arg(freg_args, freg_end, stack_args, &s->val.f);
                    break;
                case nsXPTType::T_DOUBLE:
                    alloc_arg(freg_args, freg_end, stack_args, &s->val.d);
                    break;
                case nsXPTType::T_I8:
                    alloc_arg(ireg_args, ireg_end, stack_args, &s->val.i8);
                    break;
                case nsXPTType::T_I16:
                    alloc_arg(ireg_args, ireg_end, stack_args, &s->val.i16);
                    break;
                case nsXPTType::T_I32:
                    alloc_arg(ireg_args, ireg_end, stack_args, &s->val.i32);
                    break;
                case nsXPTType::T_I64:
                    alloc_arg(ireg_args, ireg_end, stack_args, &s->val.i64);
                    break;
                case nsXPTType::T_U8:
                    alloc_arg(ireg_args, ireg_end, stack_args, &s->val.u8);
                    break;
                case nsXPTType::T_U16:
                    alloc_arg(ireg_args, ireg_end, stack_args, &s->val.u16);
                    break;
                case nsXPTType::T_U32:
                    alloc_arg(ireg_args, ireg_end, stack_args, &s->val.u32);
                    break;
                case nsXPTType::T_U64:
                    alloc_arg(ireg_args, ireg_end, stack_args, &s->val.u64);
                    break;
                case nsXPTType::T_BOOL:
                    alloc_arg(ireg_args, ireg_end, stack_args, &s->val.b);
                    break;
                case nsXPTType::T_CHAR:
                    alloc_arg(ireg_args, ireg_end, stack_args, &s->val.c);
                    break;
                case nsXPTType::T_WCHAR:
                    alloc_arg(ireg_args, ireg_end, stack_args, &s->val.wc);
                    break;
                default:
                    // all the others are plain pointer types
                    alloc_arg(ireg_args, ireg_end, stack_args, &s->val.p);
                    break;
            }
        }
    }
}

extern "C" nsresult _NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
                                      uint32_t paramCount, nsXPTCVariant* params);

EXPORT_XPCOM_API(nsresult)
NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
                 uint32_t paramCount, nsXPTCVariant* params)
{
    return _NS_InvokeByIndex(that, methodIndex, paramCount, params);
}