Bug 963024 - AArch64 support for XPCOM. r=froydnj
authorMarcin Juszkiewicz <mjuszkiewicz@redhat.com>
Wed, 26 Feb 2014 13:43:16 -0500
changeset 170795 d56b5c1a557348d4ac14a4d1ea7a5b5d240e3647
parent 170794 6a46f53ad944b44385398822d7bcf7621a785d91
child 170796 9d58d9a2c8b168dc17c45c79cbd5053260cf1d4b
push id26301
push userkwierso@gmail.com
push dateThu, 27 Feb 2014 02:21:01 +0000
treeherdermozilla-central@de2ce8226ca8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs963024
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 963024 - AArch64 support for XPCOM. r=froydnj
xpcom/reflect/xptcall/src/md/unix/moz.build
xpcom/reflect/xptcall/src/md/unix/xptcinvoke_aarch64.cpp
xpcom/reflect/xptcall/src/md/unix/xptcinvoke_asm_aarch64.s
xpcom/reflect/xptcall/src/md/unix/xptcstubs_aarch64.cpp
xpcom/reflect/xptcall/src/md/unix/xptcstubs_asm_aarch64.s
--- a/xpcom/reflect/xptcall/src/md/unix/moz.build
+++ b/xpcom/reflect/xptcall/src/md/unix/moz.build
@@ -144,16 +144,23 @@ if CONFIG['OS_ARCH'] == 'NetBSD':
     if CONFIG['OS_TEST'] in ('amiga', 'atari', 'hp300', 'mac68k', 'mvme68k',
                              'next68k', 'sun3', 'sun3x', 'x68k'):
         SOURCES += [
             'xptcinvoke_netbsd_m68k.cpp',
             'xptcstubs_netbsd_m68k.cpp'
         ]
 
 if CONFIG['OS_ARCH'] == 'Linux':
+    if CONFIG['OS_TEST'] == 'aarch64':
+        SOURCES += [
+            'xptcinvoke_aarch64.cpp',
+            'xptcinvoke_asm_aarch64.s',
+            'xptcstubs_aarch64.cpp',
+            'xptcstubs_asm_aarch64.s',
+        ]
     if CONFIG['OS_TEST'] == 'm68k':
         SOURCES += [
             'xptcinvoke_linux_m68k.cpp',
             'xptcstubs_linux_m68k.cpp',
         ]
     if CONFIG['OS_TEST'].find('mips') != -1:
         if CONFIG['OS_TEST'].find('mips64') != -1:
             SOURCES += [
new file mode 100644
--- /dev/null
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_aarch64.cpp
@@ -0,0 +1,136 @@
+/* -*- 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
+
+/*
+ * Allocation of integer function arguments initially to registers r1-r7
+ * and then to stack. Handling of 'that' argument which goes to register r0
+ * is handled separately and does not belong here.
+ *
+ * 'ireg_args'  - pointer to the current position in the buffer,
+ *                corresponding to the register arguments
+ * 'stack_args' - pointer to the current position in the buffer,
+ *                corresponding to the arguments on stack
+ * 'end'        - pointer to the end of the registers argument
+ *                buffer.
+ */
+static inline void alloc_word(uint64_t* &ireg_args,
+                              uint64_t* &stack_args,
+                              uint64_t* end,
+                              uint64_t  data)
+{
+    if (ireg_args < end) {
+        *ireg_args = data;
+        ireg_args++;
+    } else {
+        *stack_args = data;
+        stack_args++;
+    }
+}
+
+static inline void alloc_double(double* &freg_args,
+                                uint64_t* &stack_args,
+                                double* end,
+                                double  data)
+{
+    if (freg_args < end) {
+        *freg_args = data;
+        freg_args++;
+    } else {
+        memcpy(stack_args, &data, sizeof(data));
+        stack_args++;
+    }
+}
+
+static inline void alloc_float(double* &freg_args,
+                               uint64_t* &stack_args,
+                               double* end,
+                               float  data)
+{
+    if (freg_args < end) {
+        memcpy(freg_args, &data, sizeof(data));
+        freg_args++;
+    } else {
+        memcpy(stack_args, &data, sizeof(data));
+        stack_args++;
+    }
+}
+
+
+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;
+    double *freg_args = (double *)ireg_end;
+    double *freg_end  = freg_args + 8;
+    uint64_t *stack_args = (uint64_t *)freg_end;
+
+    // leave room for 'that' argument in x0
+    ++ireg_args;
+
+    for (uint32_t i = 0; i < paramCount; i++, s++) {
+        if (s->IsPtrData()) {
+            alloc_word(ireg_args, stack_args, ireg_end, (uint64_t)s->ptr);
+            continue;
+        }
+        // According to the ABI, integral types that are smaller than 8 bytes
+        // are to be passed in 8-byte registers or 8-byte stack slots.
+        switch (s->type) {
+            case nsXPTType::T_FLOAT:
+                alloc_float(freg_args, stack_args, freg_end, s->val.f);
+                break;
+            case nsXPTType::T_DOUBLE:
+                alloc_double(freg_args, stack_args, freg_end, s->val.d);
+                break;
+            case nsXPTType::T_I8:  alloc_word(ireg_args, stk, end, s->val.i8);   break;
+            case nsXPTType::T_I16: alloc_word(ireg_args, stk, end, s->val.i16);  break;
+            case nsXPTType::T_I32: alloc_word(ireg_args, stk, end, s->val.i32);  break;
+            case nsXPTType::T_I64: alloc_word(ireg_args, stk, end, s->val.i64);  break;
+            case nsXPTType::T_U8:  alloc_word(ireg_args, stk, end, s->val.u8);   break;
+            case nsXPTType::T_U16: alloc_word(ireg_args, stk, end, s->val.u16);  break;
+            case nsXPTType::T_U32: alloc_word(ireg_args, stk, end, s->val.u32);  break;
+            case nsXPTType::T_U64: alloc_word(ireg_args, stk, end, s->val.u64);  break;
+            case nsXPTType::T_BOOL: alloc_word(ireg_args, stk, end, s->val.b);   break;
+            case nsXPTType::T_CHAR: alloc_word(ireg_args, stk, end, s->val.c);   break;
+            case nsXPTType::T_WCHAR: alloc_word(ireg_args, stk, end, s->val.wc); break;
+            default:
+                // all the others are plain pointer types
+                alloc_word(ireg_args, stack_args, ireg_end,
+                           reinterpret_cast<uint64_t>(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);
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_asm_aarch64.s
@@ -0,0 +1,67 @@
+/* -*- 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/. */
+
+        .section ".text"
+            .globl _NS_InvokeByIndex
+            .type  _NS_InvokeByIndex,@function
+
+/*
+ * _NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
+ *                   uint32_t paramCount, nsXPTCVariant* params)
+ */
+
+_NS_InvokeByIndex:
+            # set up frame
+            stp         x29, x30, [sp,#-32]!
+            mov         x29, sp
+            stp         x19, x20, [sp,#16]
+
+            # save methodIndex across function calls
+            mov         w20, w1
+
+            # end of stack area passed to invoke_copy_to_stack
+            mov         x1, sp
+
+            # assume 8 bytes of stack for each argument with 16-byte alignment
+            add         w19, w2, #1
+            and         w19, w19, #0xfffffffe
+            sub         sp, sp, w19, uxth #3
+
+            # temporary place to store args passed in r0-r7,v0-v7
+            sub         sp, sp, #128
+
+            # save 'that' on stack
+            str         x0, [sp]
+
+            # start of stack area passed to invoke_copy_to_stack
+            mov         x0, sp
+            bl          invoke_copy_to_stack
+
+            # load arguments passed in r0-r7
+            ldp         x6, x7, [sp, #48]
+            ldp         x4, x5, [sp, #32]
+            ldp         x2, x3, [sp, #16]
+            ldp         x0, x1, [sp],#64
+
+            # load arguments passed in v0-v7
+            ldp         d6, d7, [sp, #48]
+            ldp         d4, d5, [sp, #32]
+            ldp         d2, d3, [sp, #16]
+            ldp         d0, d1, [sp],#64
+
+            # call the method
+            ldr         x16, [x0]
+            add         x16, x16, w20, uxth #3
+            ldr         x16, [x16]
+            blr         x16
+
+            add         sp, sp, w19, uxth #3
+            ldp         x19, x20, [sp,#16]
+            ldp         x29, x30, [sp],#32
+            ret
+
+            .size _NS_InvokeByIndex, . - _NS_InvokeByIndex
+
+
new file mode 100644
--- /dev/null
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_aarch64.cpp
@@ -0,0 +1,219 @@
+/* -*- 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/. */
+
+#include "xptcprivate.h"
+#include "xptiprivate.h"
+
+#ifndef __AARCH64EL__
+#error "Only little endian compatibility was tested"
+#endif
+
+/*
+ * This is for AArch64 ABI
+ *
+ * When we're called, the "gp" registers are stored in gprData and
+ * the "fp" registers are stored in fprData. Each array has 8 regs
+ * but first reg in gprData is a placeholder for 'self'.
+ */
+extern "C" nsresult
+PrepareAndDispatch(nsXPTCStubBase* self, uint32_t methodIndex, uint64_t* args,
+                   uint64_t *gprData, double *fprData)
+{
+#define PARAM_BUFFER_COUNT        16
+#define PARAM_GPR_COUNT            8
+#define PARAM_FPR_COUNT            8
+
+    nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
+    nsXPTCMiniVariant* dispatchParams = NULL;
+    const nsXPTMethodInfo* info;
+    nsresult result = NS_ERROR_FAILURE;
+
+    NS_ASSERTION(self,"no self");
+
+    self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
+    NS_ASSERTION(info,"no method info");
+
+    uint32_t paramCount = info->GetParamCount();
+
+    // setup variant array pointer
+    if (paramCount > PARAM_BUFFER_COUNT) {
+        dispatchParams = new nsXPTCMiniVariant[paramCount];
+    } else {
+        dispatchParams = paramBuffer;
+    }
+    NS_ASSERTION(dispatchParams,"no place for params");
+
+    uint64_t* ap = args;
+    uint32_t next_gpr = 1; // skip first arg which is 'self'
+    uint32_t next_fpr = 0;
+    for (uint32_t i = 0; i < paramCount; i++) {
+        const nsXPTParamInfo& param = info->GetParam(i);
+        const nsXPTType& type = param.GetType();
+        nsXPTCMiniVariant* dp = &dispatchParams[i];
+
+        if (param.IsOut() || !type.IsArithmetic()) {
+            if (next_gpr < PARAM_GPR_COUNT) {
+                dp->val.p = (void*)gprData[next_gpr++];
+            } else {
+                dp->val.p = (void*)*ap++;
+            }
+            continue;
+        }
+
+        switch (type) {
+            case nsXPTType::T_I8:
+                if (next_gpr < PARAM_GPR_COUNT) {
+                    dp->val.i8  = (int8_t)gprData[next_gpr++];
+                } else {
+                    dp->val.i8  = (int8_t)*ap++;
+                }
+                break;
+
+            case nsXPTType::T_I16:
+                if (next_gpr < PARAM_GPR_COUNT) {
+                    dp->val.i16  = (int16_t)gprData[next_gpr++];
+                } else {
+                    dp->val.i16  = (int16_t)*ap++;
+                }
+                break;
+
+            case nsXPTType::T_I32:
+                if (next_gpr < PARAM_GPR_COUNT) {
+                    dp->val.i32  = (int32_t)gprData[next_gpr++];
+                } else {
+                    dp->val.i32  = (int32_t)*ap++;
+                }
+                break;
+
+            case nsXPTType::T_I64:
+                if (next_gpr < PARAM_GPR_COUNT) {
+                    dp->val.i64  = (int64_t)gprData[next_gpr++];
+                } else {
+                    dp->val.i64  = (int64_t)*ap++;
+                }
+                break;
+
+            case nsXPTType::T_U8:
+                if (next_gpr < PARAM_GPR_COUNT) {
+                    dp->val.u8  = (uint8_t)gprData[next_gpr++];
+                } else {
+                    dp->val.u8  = (uint8_t)*ap++;
+                }
+                break;
+
+            case nsXPTType::T_U16:
+                if (next_gpr < PARAM_GPR_COUNT) {
+                    dp->val.u16  = (uint16_t)gprData[next_gpr++];
+                } else {
+                    dp->val.u16  = (uint16_t)*ap++;
+                }
+                break;
+
+            case nsXPTType::T_U32:
+                if (next_gpr < PARAM_GPR_COUNT) {
+                    dp->val.u32  = (uint32_t)gprData[next_gpr++];
+                } else {
+                    dp->val.u32  = (uint32_t)*ap++;
+                }
+                break;
+
+            case nsXPTType::T_U64:
+                if (next_gpr < PARAM_GPR_COUNT) {
+                    dp->val.u64  = (uint64_t)gprData[next_gpr++];
+                } else {
+                    dp->val.u64  = (uint64_t)*ap++;
+                }
+                break;
+
+            case nsXPTType::T_FLOAT:
+                if (next_fpr < PARAM_FPR_COUNT) {
+                    memcpy(&dp->val.f, &fprData[next_fpr++], sizeof(dp->val.f));
+                } else {
+                    memcpy(&dp->val.f, ap++, sizeof(dp->val.f));
+                }
+                break;
+
+            case nsXPTType::T_DOUBLE:
+                if (next_fpr < PARAM_FPR_COUNT) {
+                    memcpy(&dp->val.d, &fprData[next_fpr++], sizeof(dp->val.d));
+                } else {
+                    memcpy(&dp->val.d, ap++, sizeof(dp->val.d));
+                }
+                break;
+
+            case nsXPTType::T_BOOL:
+                if (next_gpr < PARAM_GPR_COUNT) {
+                    dp->val.b  = (bool)gprData[next_gpr++];
+                } else {
+                    dp->val.b  = (bool)*ap++;
+                }
+                break;
+
+            case nsXPTType::T_CHAR:
+                if (next_gpr < PARAM_GPR_COUNT) {
+                    dp->val.c  = (char)gprData[next_gpr++];
+                } else {
+                    dp->val.c  = (char)*ap++;
+                }
+                break;
+
+            case nsXPTType::T_WCHAR:
+                if (next_gpr < PARAM_GPR_COUNT) {
+                    dp->val.wc  = (wchar_t)gprData[next_gpr++];
+                } else {
+                    dp->val.wc  = (wchar_t)*ap++;
+                }
+                break;
+
+            default:
+                NS_ASSERTION(0, "bad type");
+                break;
+        }
+    }
+
+    result = self->mOuter->CallMethod((uint16_t)methodIndex, info, dispatchParams);
+
+    if (dispatchParams != paramBuffer) {
+        delete [] dispatchParams;
+    }
+
+    return result;
+}
+
+// Load w17 with the constant 'n' and branch to SharedStub().
+# define STUB_ENTRY(n)                                                  \
+    __asm__ (                                                           \
+            ".section \".text\" \n\t"                                   \
+            ".align 2\n\t"                                              \
+            ".if "#n" < 10 \n\t"                                        \
+            ".globl  _ZN14nsXPTCStubBase5Stub"#n"Ev \n\t"               \
+            ".hidden _ZN14nsXPTCStubBase5Stub"#n"Ev \n\t"               \
+            ".type   _ZN14nsXPTCStubBase5Stub"#n"Ev,@function \n\n"     \
+            "_ZN14nsXPTCStubBase5Stub"#n"Ev: \n\t"                      \
+            ".elseif "#n" < 100 \n\t"                                   \
+            ".globl  _ZN14nsXPTCStubBase6Stub"#n"Ev \n\t"               \
+            ".hidden _ZN14nsXPTCStubBase6Stub"#n"Ev \n\t"               \
+            ".type   _ZN14nsXPTCStubBase6Stub"#n"Ev,@function \n\n"     \
+            "_ZN14nsXPTCStubBase6Stub"#n"Ev: \n\t"                      \
+            ".elseif "#n" < 1000 \n\t"                                  \
+            ".globl  _ZN14nsXPTCStubBase7Stub"#n"Ev \n\t"               \
+            ".hidden _ZN14nsXPTCStubBase7Stub"#n"Ev \n\t"               \
+            ".type   _ZN14nsXPTCStubBase7Stub"#n"Ev,@function \n\n"     \
+            "_ZN14nsXPTCStubBase7Stub"#n"Ev: \n\t"                      \
+            ".else  \n\t"                                               \
+            ".err   \"stub number "#n" >= 1000 not yet supported\"\n"   \
+            ".endif \n\t"                                               \
+            "mov    w17,#"#n" \n\t"                                     \
+            "b      SharedStub \n"                                      \
+);
+
+#define SENTINEL_ENTRY(n)                              \
+    nsresult nsXPTCStubBase::Sentinel##n()             \
+{                                                      \
+    NS_ASSERTION(0,"nsXPTCStubBase::Sentinel called"); \
+    return NS_ERROR_NOT_IMPLEMENTED;                   \
+}
+
+#include "xptcstubsdef.inc"
new file mode 100644
--- /dev/null
+++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_asm_aarch64.s
@@ -0,0 +1,39 @@
+# 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/.
+
+            .set NGPREGS,8
+            .set NFPREGS,8
+
+            .section ".text"
+            .globl SharedStub
+            .hidden SharedStub
+            .type  SharedStub,@function
+SharedStub:
+            stp         x29, x30, [sp,#-16]!
+            mov         x29, sp
+
+            sub         sp, sp, #8*(NGPREGS+NFPREGS)
+            stp         x0, x1, [sp, #64+(0*8)]
+            stp         x2, x3, [sp, #64+(2*8)]
+            stp         x4, x5, [sp, #64+(4*8)]
+            stp         x6, x7, [sp, #64+(6*8)]
+            stp         d0, d1, [sp, #(0*8)]
+            stp         d2, d3, [sp, #(2*8)]
+            stp         d4, d5, [sp, #(4*8)]
+            stp         d6, d7, [sp, #(6*8)]
+
+            # methodIndex passed from stub
+            mov         w1, w17
+
+            add         x2, sp, #16+(8*(NGPREGS+NFPREGS))
+            add         x3, sp, #8*NFPREGS
+            add         x4, sp, #0
+
+            bl          PrepareAndDispatch
+
+            add         sp, sp, #8*(NGPREGS+NFPREGS)
+            ldp         x29, x30, [sp],#16
+            ret
+
+            .size SharedStub, . - SharedStub