This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 9c15e1abd703a7953928d7a80655ae1a76a41f6d Author: Pedro Giffuni <[email protected]> AuthorDate: Sat Sep 12 00:25:13 2026 -0500 freebsd_aarch64: Initial support for FreeBSD ARM64 This is early WIP, based on the macOS Silicon port. It is made available only because it is easier to get working once there is a rough sketch. Produced by Code Copilot. --- main/bridges/Library_cpp_uno.mk | 15 + .../source/cpp_uno/gcc3_freebsd_aarch64/abi.cxx | 312 +++++++++++++++ .../source/cpp_uno/gcc3_freebsd_aarch64/abi.hxx | 96 +++++ .../source/cpp_uno/gcc3_freebsd_aarch64/call.s | 197 +++++++++ .../cpp_uno/gcc3_freebsd_aarch64/cpp2uno.cxx | 438 +++++++++++++++++++++ .../source/cpp_uno/gcc3_freebsd_aarch64/except.cxx | 253 ++++++++++++ .../cpp_uno/gcc3_freebsd_aarch64/makefile.mk | 75 ++++ .../source/cpp_uno/gcc3_freebsd_aarch64/share.hxx | 65 +++ .../cpp_uno/gcc3_freebsd_aarch64/uno2cpp.cxx | 189 +++++++++ 9 files changed, 1640 insertions(+) diff --git a/main/bridges/Library_cpp_uno.mk b/main/bridges/Library_cpp_uno.mk index 0d8525362a..e3bce187d3 100644 --- a/main/bridges/Library_cpp_uno.mk +++ b/main/bridges/Library_cpp_uno.mk @@ -156,6 +156,21 @@ $(eval $(call gb_Library_add_asmobjects,$(COMNAME)_uno,\ bridges/source/cpp_uno/gcc3_freebsd_x86-64/call \ )) +########################################################### +else ifeq ($(OS)-$(CPUNAME)-$(COMNAME),FREEBSD-AARCH64-gcc3) +########################################################### + +$(eval $(call gb_Library_add_exception_objects,$(COMNAME)_uno,\ + bridges/source/cpp_uno/gcc3_freebsd_aarch64/abi \ + bridges/source/cpp_uno/gcc3_freebsd_aarch64/except \ + bridges/source/cpp_uno/gcc3_freebsd_aarch64/cpp2uno \ + bridges/source/cpp_uno/gcc3_freebsd_aarch64/uno2cpp \ +)) + +$(eval $(call gb_Library_add_asmobjects,$(COMNAME)_uno,\ + bridges/source/cpp_uno/gcc3_freebsd_aarch64/call \ +)) + ######################################################### else ifeq ($(OS)-$(CPUNAME)-$(COMNAME),LINUX-ALPHA_-gcc3) ######################################################### diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/abi.cxx b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/abi.cxx new file mode 100644 index 0000000000..71b8692f79 --- /dev/null +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/abi.cxx @@ -0,0 +1,312 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_bridges.hxx" + +// This is an implementation of the parameter-classification rules of the +// AArch64 procedure call standard ("Procedure Call Standard for the Arm 64-bit +// Architecture", ARM IHI 0055). +// +// Unlike the System V AMD64 ABI (used by the x86-64 bridge), AAPCS64 does not +// split aggregates into per-eightbyte INTEGER/SSE classes. Instead: +// * scalars go in one GPR (x) or one FP/SIMD (v) register; +// * a Homogeneous Floating-point Aggregate (HFA: <= 4 members, all the same +// FP type, recursively) goes in consecutive v registers; +// * any other aggregate <= 16 bytes goes in 1-2 GPRs; +// * a non-HFA aggregate > 16 bytes is passed indirectly (a pointer to a +// caller-allocated copy). +// Register fill is "all or nothing": if an aggregate does not fit entirely in +// the remaining registers of its bank, it is passed wholly on the stack. +// +// This is a clean-room implementation from the public specifications; see +// ../../../../AAPCS64_BRIDGE_SPEC.md. + +#include "abi.hxx" + +#include "bridges/cpp_uno/shared/types.hxx" + +#include <rtl/ustring.hxx> +#include <string.h> + +using namespace aarch64; + +namespace { + +// The element type of a Homogeneous Floating-point Aggregate. +enum HfaKind +{ + HFA_NONE, // not (yet) an HFA + HFA_FLOAT, // all members are FLOAT (4-byte) + HFA_DOUBLE // all members are DOUBLE (8-byte) +}; + +// Combine the running HFA kind with a newly-seen member kind. Two members +// of different FP types, or any non-FP member, break the homogeneity. +HfaKind mergeHfa( HfaKind running, HfaKind seen ) +{ + if ( seen == HFA_NONE ) + return HFA_NONE; + if ( running == HFA_NONE ) + return seen; + return ( running == seen ) ? running : HFA_NONE; +} + +bool isComplexAggregate( typelib_TypeDescriptionReference *pTypeRef ) +{ + typelib_TypeDescription * pTypeDescr = 0; + TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); + const typelib_CompoundTypeDescription *pComp = + reinterpret_cast<const typelib_CompoundTypeDescription *>( pTypeDescr ); + bool complex = pComp->pBaseTypeDescription != 0 && + isComplexAggregate( pComp->pBaseTypeDescription->aBase.pWeakRef ); + for ( sal_Int32 i = 0; !complex && i < pComp->nMembers; ++i ) + { + typelib_TypeClass typeClass = pComp->ppTypeRefs[i]->eTypeClass; + if ( typeClass == typelib_TypeClass_STRUCT || + typeClass == typelib_TypeClass_EXCEPTION ) + complex = isComplexAggregate( pComp->ppTypeRefs[i] ); + else + complex = !bridges::cpp_uno::shared::isSimpleType( typeClass ); + } + TYPELIB_DANGER_RELEASE( pTypeDescr ); + return complex; +} + +// Recursively determine whether pTypeRef is (part of) a homogeneous +// floating-point aggregate, accumulating the element kind and member count. +// +// Returns false the moment homogeneity is violated (a non-FP scalar, or a +// second distinct FP type, or > 4 elements). A FLOAT/DOUBLE scalar counts as +// a 1-element HFA of itself; a struct flattens its members (and base classes). +bool collectHfa( typelib_TypeDescriptionReference *pTypeRef, HfaKind &rKind, int &rCount ) +{ + switch ( pTypeRef->eTypeClass ) + { + case typelib_TypeClass_FLOAT: + rKind = mergeHfa( rKind, HFA_FLOAT ); + if ( rKind == HFA_NONE ) return false; + return ( ++rCount <= 4 ); + + case typelib_TypeClass_DOUBLE: + rKind = mergeHfa( rKind, HFA_DOUBLE ); + if ( rKind == HFA_NONE ) return false; + return ( ++rCount <= 4 ); + + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + typelib_TypeDescription * pTypeDescr = 0; + TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); + + const typelib_CompoundTypeDescription *pComp = + reinterpret_cast<const typelib_CompoundTypeDescription*>( pTypeDescr ); + + // rCount is cumulative over the whole recursion, so remember where + // this aggregate started in order to size-check it below. + const int nCountAtEntry = rCount; + bool bOk = true; + + // Flatten base class first (its members precede ours in layout). + if ( pComp->pBaseTypeDescription ) + { + bOk = collectHfa( + pComp->pBaseTypeDescription->aBase.pWeakRef, rKind, rCount ); + } + + for ( sal_Int32 i = 0; bOk && i < pComp->nMembers; ++i ) + bOk = collectHfa( pComp->ppTypeRefs[i], rKind, rCount ); + + if ( bOk ) + { + // Reject anything the elements do not tile exactly: only the + // elements contributed by THIS aggregate count towards its size. + sal_Int32 elementSize = rKind == HFA_FLOAT ? 4 : 8; + bOk = pTypeDescr->nSize == + ( rCount - nCountAtEntry ) * elementSize; + for ( sal_Int32 i = 0; bOk && i < pComp->nMembers; ++i ) + bOk = pComp->pMemberOffsets[i] % elementSize == 0; + } + + TYPELIB_DANGER_RELEASE( pTypeDescr ); + return bOk; + } + + default: + // Any non-FP, non-aggregate member breaks homogeneity. + rKind = HFA_NONE; + return false; + } +} + +// Classify an aggregate (STRUCT/EXCEPTION). Sets the GPR/FPR counts and +// returns true if it is passed in registers, false if it must be passed +// indirectly (in memory). +bool classifyAggregate( typelib_TypeDescriptionReference *pTypeRef, int &nUsedGPR, int &nUsedFPR ) +{ + // First, the HFA test. + HfaKind kind = HFA_NONE; + int count = 0; + if ( collectHfa( pTypeRef, kind, count ) && kind != HFA_NONE ) + { + nUsedFPR = count; + nUsedGPR = 0; + return true; // HFA passed in consecutive FP regs + } + + // Not HFA: if bigger than 16 bytes, pass indirectly. + typelib_TypeDescription * pTypeDescr = 0; + TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); + if ( pTypeDescr->nSize > 16 ) + { + TYPELIB_DANGER_RELEASE( pTypeDescr ); + nUsedGPR = nUsedFPR = 0; + return false; // indirect + } + + // small aggregate: it occupies 1 or 2 GPRs depending on size + nUsedFPR = 0; + nUsedGPR = ( pTypeDescr->nSize + 7 ) / 8; + if ( nUsedGPR < 1 ) nUsedGPR = 1; + TYPELIB_DANGER_RELEASE( pTypeDescr ); + return true; +} + +} // anonymous namespace + +// Public API implementations. +namespace aarch64 +{ + +bool examine_argument( typelib_TypeDescriptionReference *pTypeRef, bool bInReturn, int &nUsedGPR, int &nUsedFPR ) +{ + // For returns, the hidden param rule uses >16 bytes for aggregates. + if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT || pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION ) + { + return classifyAggregate( pTypeRef, nUsedGPR, nUsedFPR ); + } + + // Scalars: floats -> FPR, others -> GPR + switch ( pTypeRef->eTypeClass ) + { + case typelib_TypeClass_FLOAT: + nUsedFPR = 1; nUsedGPR = 0; return true; + case typelib_TypeClass_DOUBLE: + nUsedFPR = 1; nUsedGPR = 0; return true; + default: + nUsedFPR = 0; nUsedGPR = 1; return true; + } +} + +bool return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef ) +{ + if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT || pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION ) + { + typelib_TypeDescription * pTypeDescr = 0; + TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); + bool ret = pTypeDescr->nSize > 16; + TYPELIB_DANGER_RELEASE( pTypeDescr ); + return ret; + } + return false; // scalars and small aggregates return in registers +} + +sal_uInt32 get_return_kind( typelib_TypeDescriptionReference *pTypeRef ) +{ + if ( pTypeRef->eTypeClass == typelib_TypeClass_FLOAT ) + return typelib_TypeClass_FLOAT; + + if ( pTypeRef->eTypeClass == typelib_TypeClass_DOUBLE ) + return typelib_TypeClass_DOUBLE; + + if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT || + pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION ) + { + HfaKind kind = HFA_NONE; + int count = 0; + + if ( collectHfa( pTypeRef, kind, count ) ) + { + if ( kind == HFA_FLOAT ) + return RETURN_KIND_HFA_FLOAT; + + if ( kind == HFA_DOUBLE ) + return RETURN_KIND_HFA_DOUBLE; + } + } + + return pTypeRef->eTypeClass; +} + +void fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal_uInt64* pGPR, const double* pFPR, void *pStruct ) +{ + // For small aggregates, copy from GPR slots; for HFAs, copy from FPR slots. + if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT || pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION ) + { + int nGPR=0, nFPR=0; + if ( classifyAggregate( pTypeRef, nGPR, nFPR ) ) + { + if ( nFPR > 0 ) + { + // HFA: copy elements from FPR slots. For FLOAT HFAs each element is + // 4 bytes but occupies an 8-byte saved slot; copy each float from the + // low 4 bytes of the corresponding double-sized slot. DOUBLE HFAs + // can be copied directly. + HfaKind kind = HFA_NONE; + int count = 0; + if ( collectHfa( pTypeRef, kind, count ) && kind == HFA_FLOAT ) + { + for ( int i = 0; i < nFPR; ++i ) + memcpy( + static_cast<char *>( pStruct ) + i * sizeof(float), + reinterpret_cast<const char *>( pFPR) + i * sizeof(double), + sizeof(float) ); + } + else + { + memcpy( pStruct, pFPR, nFPR * sizeof(double) ); + } + } + else + { + memcpy( pStruct, pGPR, nGPR * sizeof(sal_uInt64) ); + } + } + } +} + +sal_uInt32 align_stack_offset( sal_uInt32 offset, typelib_TypeDescriptionReference *pTypeRef ) +{ + // AArch64 stack overflow area is packed; align to natural alignment of the type (8) + const sal_uInt32 align = 8; + return ( offset + align - 1 ) & ~( align - 1 ); +} + +sal_uInt32 stack_size( typelib_TypeDescriptionReference *pTypeRef ) +{ + // For simple types and small aggregates, size is rounded to 8 + typelib_TypeDescription * pTypeDescr = 0; + TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); + sal_uInt32 size = pTypeDescr->nSize; + TYPELIB_DANGER_RELEASE( pTypeDescr ); + return ( size + 7 ) & ~7u; +} + +} // namespace aarch64 diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/abi.hxx b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/abi.hxx new file mode 100644 index 0000000000..e6842af737 --- /dev/null +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/abi.hxx @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +#ifndef _BRIDGES_CPP_UNO_AARCH64_ABI_HXX_ +#define _BRIDGES_CPP_UNO_AARCH64_ABI_HXX_ + +// This is an implementation of the AArch64 procedure call standard, as +// described in "Procedure Call Standard for the Arm 64-bit Architecture" +// (ARM IHI 0055). It is a clean-room implementation written from that +// public specification; see ../../../../AAPCS64_BRIDGE_SPEC.md. + +#include <typelib/typedescription.hxx> + +namespace aarch64 +{ + +/* 8 general purpose registers (x0..x7) are used for parameter passing. + Note: the indirect-result-location register x8 is *separate* and is NOT + part of this count. */ +const sal_uInt32 MAX_GPR_REGS = 8; + +/* 8 SIMD/FP registers (v0..v7) are used for parameter passing. */ +const sal_uInt32 MAX_FPR_REGS = 8; + +/* The largest number of registers a single aggregate can occupy: an HFA/HVA + may use up to 4 FP registers; a non-HFA aggregate passed in GPRs uses at + most 2 (16 bytes / 8). */ +const sal_uInt32 MAX_AGGREGATE_REGS = 4; + +enum ReturnKind +{ + RETURN_KIND_HFA_FLOAT = 0x100, + RETURN_KIND_HFA_DOUBLE = 0x101 +}; + +/* Count the number of registers required to pass the given type. + + Examines the argument and sets the number of GPR (x) and FPR (v) registers + it would consume. For a Homogeneous Floating-point Aggregate the FPR count + is the number of members (<= 4); for a non-HFA aggregate <= 16 bytes the GPR + count is 1 or 2; scalars use exactly one register of the appropriate bank. + + Returns false iff the parameter must be passed indirectly (in memory): a + non-HFA aggregate larger than 16 bytes. When bInReturn is true the same + classification answers "can this be returned in registers?" (false => the + caller must allocate a buffer and pass it in x8). +*/ +bool examine_argument( typelib_TypeDescriptionReference *pTypeRef, bool bInReturn, int &nUsedGPR, int &nUsedFPR ); + +/** Does a function returning this type use the hidden indirect-result pointer + (passed by the caller in x8), or can it return in registers? + + A scalar returns in x0 or v0; an HFA returns in v0..v3; a non-HFA aggregate + of <= 16 bytes returns in x0,x1. Anything larger (non-HFA aggregate + > 16 bytes) is returned via the caller-allocated buffer addressed by x8 - + that is the "hidden param" case, for which this returns true. +*/ +bool return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef ); + +/** Return the assembly return kind for an HFA, or the type class otherwise. */ +sal_uInt32 get_return_kind( typelib_TypeDescriptionReference *pTypeRef ); + +/** Scatter a register-resident return value (an HFA returned in v0..v3, or a + non-HFA aggregate <= 16 bytes returned in x0,x1) into the caller's struct. + + pGPR points at the saved x0,x1,... ; pFPR at the saved v0,v1,... (each + element the low 8 bytes of a v register, i.e. a double slot). Only valid + when return_in_hidden_param() is false. +*/ +void fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal_uInt64* pGPR, const double* pFPR, void *pStruct ); + +sal_uInt32 align_stack_offset( + sal_uInt32 offset, typelib_TypeDescriptionReference *pTypeRef ); + +sal_uInt32 stack_size( typelib_TypeDescriptionReference *pTypeRef ); + +} // namespace aarch64 + +#endif // _BRIDGES_CPP_UNO_AARCH64_ABI_HXX_ diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/call.s b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/call.s new file mode 100644 index 0000000000..8eb2c6449e --- /dev/null +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/call.s @@ -0,0 +1,197 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// AArch64 (System V / FreeBSD) outgoing-call trampoline for the C++-UNO +// bridge. Loads registers from caller-prepared arrays, copies overflow args +// to the outgoing stack, performs the indirect call and stores return regs. + + .text + .align 2 + .globl callVirtualFunction + .type callVirtualFunction, @function +callVirtualFunction: + .cfi_startproc + // prologue: save fp/lr and the callee-saved registers we use + stp x29, x30, [sp, #-16]! + stp x19, x20, [sp, #-16]! + stp x21, x22, [sp, #-16]! + stp x23, x24, [sp, #-16]! + mov x29, sp + .cfi_def_cfa x29, 64 + .cfi_offset x29, -16 + .cfi_offset x30, -8 + .cfi_offset x19, -32 + .cfi_offset x20, -24 + .cfi_offset x21, -48 + .cfi_offset x22, -40 + .cfi_offset x23, -64 + .cfi_offset x24, -56 + + // stash inputs that must survive the call into callee-saved registers + mov x19, x0 // pFunction + mov x20, x2 // pGPR + mov x21, x3 // pFPR + mov x22, x6 // pGPRReturn + mov x23, x7 // pFPRReturn + mov x24, x1 // x8 indirect-result value + + // allocate and copy the outgoing overflow stack arguments. + add x9, x5, #15 + bic x9, x9, #15 + sub sp, sp, x9 + mov x10, #0 +Lcvf_copy: + cmp x10, x5 + b.ge Lcvf_copied + ldrb w11, [x4, x10] + strb w11, [sp, x10] + add x10, x10, #1 + b Lcvf_copy +Lcvf_copied: + + // load the FP/SIMD argument registers d0..d7 + ldp d0, d1, [x21, #0] + ldp d2, d3, [x21, #16] + ldp d4, d5, [x21, #32] + ldp d6, d7, [x21, #48] + + // load the GP argument registers x0..x7 and the x8 indirect-result reg + mov x8, x24 + ldp x6, x7, [x20, #48] + ldp x4, x5, [x20, #32] + ldp x2, x3, [x20, #16] + ldp x0, x1, [x20, #0] + + // perform the virtual call + blr x19 + + // store the return registers + str x0, [x22, #0] + str x1, [x22, #8] + str d0, [x23, #0] + str d1, [x23, #8] + str d2, [x23, #16] + str d3, [x23, #24] + + // epilogue + mov sp, x29 + ldp x23, x24, [sp], #16 + ldp x21, x22, [sp], #16 + ldp x19, x20, [sp], #16 + ldp x29, x30, [sp], #16 + ret + .cfi_endproc + +// --------------------------------------------------------------------------- +// privateSnippetExecutor: incoming (cpp2uno) register-spill executor. + + .globl privateSnippetExecutor + .type privateSnippetExecutor, @function +privateSnippetExecutor: + .cfi_startproc + mov x17, sp // x17 = ovrflw (incoming stack args) + stp x29, x30, [sp, #-176]! + mov x29, sp + .cfi_def_cfa x29, 176 + .cfi_offset x29, -176 + .cfi_offset x30, -168 + + stp x0, x1, [sp, #16] // save GP argument registers x0..x7 + stp x2, x3, [sp, #32] + stp x4, x5, [sp, #48] + stp x6, x7, [sp, #64] + + stp d0, d1, [sp, #80] // save FP/SIMD argument registers d0..d7 + stp d2, d3, [sp, #96] + stp d4, d5, [sp, #112] + stp d6, d7, [sp, #128] + + mov w0, w16 // nFunctionIndex (low 32 bits) + lsr x1, x16, #32 // nVtableOffset (high 32 bits) + add x2, sp, #16 // gpreg + add x3, sp, #80 // fpreg + mov x4, x17 // ovrflw + mov x5, x8 // pIndirectReturn (x8 indirect-result reg) + add x6, sp, #144 // pRegisterReturn (32-byte buffer) + bl cpp_vtable_call + + cmp w0, #0x100 // RETURN_KIND_HFA_FLOAT + b.eq Lpse_hfa_float + cmp w0, #0x101 // RETURN_KIND_HFA_DOUBLE + b.eq Lpse_hfa_double + cmp w0, #10 // typelib_TypeClass_FLOAT + b.eq Lpse_float + cmp w0, #11 // typelib_TypeClass_DOUBLE + b.eq Lpse_float + cmp w0, #3 // typelib_TypeClass_BYTE + b.eq Lpse_signed_byte + cmp w0, #4 // typelib_TypeClass_SHORT + b.eq Lpse_signed_short + cmp w0, #1 // typelib_TypeClass_VOID + b.eq Lpse_void + + // integer / pointer return + ldr x0, [x6, #0] + ldr x1, [x6, #8] + ldr d0, [x6, #16] + ldr d1, [x6, #24] + b Lpse_finish + +Lpse_hfa_float: + // HFA float: up to 4 floats in d0..d3 + ldr d0, [x6, #0] + ldr d1, [x6, #8] + ldr d2, [x6, #16] + ldr d3, [x6, #24] + b Lpse_finish + +Lpse_hfa_double: + // HFA double: up to 4 doubles in d0..d3 + ldr d0, [x6, #0] + ldr d1, [x6, #8] + ldr d2, [x6, #16] + ldr d3, [x6, #24] + b Lpse_finish + +Lpse_float: + // single float/double returned in d0 + ldr d0, [x6, #16] + b Lpse_finish + +Lpse_signed_byte: + ldr x0, [x6, #0] + b Lpse_finish + +Lpse_signed_short: + ldr x0, [x6, #0] + b Lpse_finish + +Lpse_void: + mov x0, #0 + b Lpse_finish + +Lpse_finish: + ldp x23, x24, [sp], #16 + ldp x21, x22, [sp], #16 + ldp x19, x20, [sp], #16 + ldp x29, x30, [sp], #16 + ret + .cfi_endproc + + .section .note.GNU-stack,"",@progbits diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/cpp2uno.cxx b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/cpp2uno.cxx new file mode 100644 index 0000000000..c6204dadf7 --- /dev/null +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/cpp2uno.cxx @@ -0,0 +1,438 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_bridges.hxx" + +#include <stdio.h> +#include <stdlib.h> +#include <hash_map> + +#include <rtl/alloc.h> +#include <osl/mutex.hxx> + +#include <com/sun/star/uno/genfunc.hxx> +#include "com/sun/star/uno/RuntimeException.hpp" +#include <uno/data.h> +#include <typelib/typedescription.hxx> + +#include "bridges/cpp_uno/shared/bridge.hxx" +#include "bridges/cpp_uno/shared/cppinterfaceproxy.hxx" +#include "bridges/cpp_uno/shared/types.hxx" +#include "bridges/cpp_uno/shared/vtablefactory.hxx" + +#include "abi.hxx" +#include "share.hxx" + +using namespace ::osl; +using namespace ::rtl; +using namespace ::com::sun::star::uno; + +//================================================================================================== + +// Perform the UNO call +// +// We must convert the parameters stored in gpreg, fpreg and ovrflw to UNO +// arguments and call pThis->getUnoI()->pDispatcher. +// +// gpreg: this, [gpr params x0..x7] (the indirect-result ptr is x8, separate) +// fpreg: [fpr params d0..d7] +// ovrflw: [gpr or fpr params (properly aligned)] +// +// On AArch64 a structure bigger than 16 bytes is returned via the buffer +// addressed by x8 (pIndirectReturn); 'this' is always x0 = gpreg[0]. +// Simple types are returned in x0,x1 (int) or d0,d1 (fp); HFAs in d0..d3; +// non-HFA structures <= 16 bytes in x0,x1. +static typelib_TypeClass cpp2uno_call( + bridges::cpp_uno::shared::CppInterfaceProxy * pThis, + const typelib_TypeDescription * pMemberTypeDescr, + typelib_TypeDescriptionReference * pReturnTypeRef, // 0 indicates void return + sal_Int32 nParams, typelib_MethodParameter * pParams, + void ** gpreg, void ** fpreg, unsigned char * ovrflw, + void * pIndirectReturn, // AArch64 x8 indirect-result pointer (0 if none) + sal_uInt64 * pRegisterReturn /* space for register return */ ) +{ + unsigned int nr_gpr = 0; //number of gpr registers used + unsigned int nr_fpr = 0; //number of fpr registers used + sal_uInt32 stackOffset = 0; + + // return + typelib_TypeDescription * pReturnTypeDescr = 0; + if (pReturnTypeRef) + TYPELIB_DANGER_GET( &pReturnTypeDescr, pReturnTypeRef ); + + void * pUnoReturn = 0; + void * pCppReturn = 0; // complex return ptr: if != 0 && != pUnoReturn, reconversion need + + if ( pReturnTypeDescr ) + { + if ( aarch64::return_in_hidden_param( pReturnTypeRef ) ) + { + // AArch64: the indirect-result pointer arrives in x8, NOT in the + // first general-purpose argument register (unlike x86-64 SysV). + // So we take it from pIndirectReturn and do NOT consume a gpreg + // slot here; 'this' still occupies gpreg[0] below. + pCppReturn = pIndirectReturn; + + pUnoReturn = ( bridges::cpp_uno::shared::relatesToInterfaceType( pReturnTypeDescr ) + ? alloca( pReturnTypeDescr->nSize ) + : pCppReturn ); // direct way + } + else + pUnoReturn = pRegisterReturn; // direct way for simple types + } + + // pop this (x0) + gpreg++; + nr_gpr++; + + // stack space + // parameters + void ** pUnoArgs = reinterpret_cast<void **>(alloca( 4 * sizeof(void *) * nParams )); + void ** pCppArgs = pUnoArgs + nParams; + // indices of values this have to be converted (interface conversion cpp<=>uno) + sal_Int32 * pTempIndizes = reinterpret_cast<sal_Int32 *>(pUnoArgs + (2 * nParams)); + // type descriptions for reconversions + typelib_TypeDescription ** ppTempParamTypeDescr = reinterpret_cast<typelib_TypeDescription **>(pUnoArgs + (3 * nParams)); + + sal_Int32 nTempIndizes = 0; + + for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) + { + const typelib_MethodParameter & rParam = pParams[nPos]; + + int nUsedGPR = 0; + int nUsedFPR = 0; + if ( !rParam.bOut && bridges::cpp_uno::shared::isSimpleType( rParam.pTypeRef ) ) // value + { + // A simple UNO type occupies exactly one register, GPR or FPR. + if ( rParam.pTypeRef->eTypeClass == typelib_TypeClass_FLOAT || + rParam.pTypeRef->eTypeClass == typelib_TypeClass_DOUBLE ) + { + nUsedFPR = 1; nUsedGPR = 0; + } + else + { + nUsedFPR = 0; nUsedGPR = 1; + } + + OSL_ASSERT( ( nUsedFPR == 1 && nUsedGPR == 0 ) || ( nUsedFPR == 0 && nUsedGPR == 1 ) ); + + if ( nUsedFPR == 1 ) + { + if ( nr_fpr < aarch64::MAX_FPR_REGS ) + { + pCppArgs[nPos] = pUnoArgs[nPos] = fpreg++; + nr_fpr++; + } + else + { + stackOffset = aarch64::align_stack_offset( + stackOffset, rParam.pTypeRef ); + pCppArgs[nPos] = pUnoArgs[nPos] = ovrflw + stackOffset; + stackOffset += aarch64::stack_size( rParam.pTypeRef ); + } + } + else if ( nUsedGPR == 1 ) + { + if ( nr_gpr < aarch64::MAX_GPR_REGS ) + { + pCppArgs[nPos] = pUnoArgs[nPos] = gpreg++; + nr_gpr++; + } + else + { + stackOffset = aarch64::align_stack_offset( + stackOffset, rParam.pTypeRef ); + pCppArgs[nPos] = pUnoArgs[nPos] = ovrflw + stackOffset; + stackOffset += aarch64::stack_size( rParam.pTypeRef ); + } + } + } + else // struct <= 16 bytes || ptr to complex value || ref + { + typelib_TypeDescription * pParamTypeDescr = 0; + TYPELIB_DANGER_GET( &pParamTypeDescr, rParam.pTypeRef ); + + void *pCppStack = 0; + if ( nr_gpr < aarch64::MAX_GPR_REGS ) + { + pCppArgs[nPos] = pCppStack = *gpreg++; + nr_gpr++; + } + else + { + stackOffset = (stackOffset + sizeof(void *) - 1) & + ~(sizeof(void *) - 1); + pCppArgs[nPos] = pCppStack = + *reinterpret_cast<void **>( ovrflw + stackOffset ); + stackOffset += sizeof(void *); + } + + if (! rParam.bIn) // is pure out + { + // uno out is unconstructed mem! + pUnoArgs[nPos] = alloca( pParamTypeDescr->nSize ); + pTempIndizes[nTempIndizes] = nPos; + // will be released at reconversion + ppTempParamTypeDescr[nTempIndizes++] = pParamTypeDescr; + } + else if ( bridges::cpp_uno::shared::relatesToInterfaceType( pParamTypeDescr ) ) // is in/inout + { + uno_copyAndConvertData( pUnoArgs[nPos] = alloca( pParamTypeDescr->nSize ), + pCppStack, pParamTypeDescr, + pThis->getBridge()->getCpp2Uno() ); + pTempIndizes[nTempIndizes] = nPos; // has to be reconverted + // will be released at reconversion + ppTempParamTypeDescr[nTempIndizes++] = pParamTypeDescr; + } + + if ( pCppStack ) + { + if ( pParamTypeDescr->eTypeClass == typelib_TypeClass_STRUCT || + pParamTypeDescr->eTypeClass == typelib_TypeClass_EXCEPTION ) + { + // struct in registers/stack: copy it to a temporary if + // it needs conversion + if ( bridges::cpp_uno::shared::relatesToInterfaceType( pParamTypeDescr ) ) + { + // already handled above + } + else + pUnoArgs[nPos] = pCppStack; + } + } + if ( pParamTypeDescr ) + TYPELIB_DANGER_RELEASE( pParamTypeDescr ); + } + } + + // ExceptionHolder + uno_Any aUnoExc; // Any will be constructed by callee + uno_Any * pUnoExc = &aUnoExc; + + // invoke uno dispatch call + (*pThis->getUnoI()->pDispatcher)( pThis->getUnoI(), pMemberTypeDescr, pUnoReturn, pUnoArgs, &pUnoExc ); + + // in case an exception occurred... + if ( pUnoExc ) + { + // destruct temporary in/inout params + for ( ; nTempIndizes--; ) + { + sal_Int32 nIndex = pTempIndizes[nTempIndizes]; + + if (pParams[nIndex].bIn) // is in/inout => was constructed + uno_destructData( pUnoArgs[nIndex], ppTempParamTypeDescr[nTempIndizes], 0 ); + TYPELIB_DANGER_RELEASE( ppTempParamTypeDescr[nTempIndizes] ); + } + if (pReturnTypeDescr) + TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); + + CPPU_CURRENT_NAMESPACE::raiseException( &aUnoExc, pThis->getBridge()->getUno2Cpp() ); // has to destruct the any + // is here for dummy + return typelib_TypeClass_VOID; + } + else // else no exception occurred... + { + // temporary params + for ( ; nTempIndizes--; ) + { + sal_Int32 nIndex = pTempIndizes[nTempIndizes]; + typelib_TypeDescription * pParamTypeDescr = ppTempParamTypeDescr[nTempIndizes]; + + if ( pParams[nIndex].bOut ) // inout/out + { + // convert and assign + uno_destructData( pCppArgs[nIndex], pParamTypeDescr, cpp_release ); + uno_copyAndConvertData( pCppArgs[nIndex], pUnoArgs[nIndex], pParamTypeDescr, + pThis->getBridge()->getUno2Cpp() ); + } + // destroy temp uno param + uno_destructData( pUnoArgs[nIndex], pParamTypeDescr, 0 ); + + TYPELIB_DANGER_RELEASE( pParamTypeDescr ); + } + // return + if ( pCppReturn ) // has complex return + { + if ( pUnoReturn != pCppReturn ) // needs reconversion + { + uno_copyAndConvertData( pCppReturn, pUnoReturn, pReturnTypeDescr, + pThis->getBridge()->getUno2Cpp() ); + // destroy temp uno return + uno_destructData( pUnoReturn, pReturnTypeDescr, 0 ); + } + // complex return ptr is set to return reg + *reinterpret_cast<void **>(pRegisterReturn) = pCppReturn; + } + if ( pReturnTypeDescr ) + { + typelib_TypeClass eRet = (typelib_TypeClass)pReturnTypeDescr->eTypeClass; + TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); + return eRet; + } + else + return typelib_TypeClass_VOID; + } +} + + +//================================================================================================== +extern "C" sal_uInt32 cpp_vtable_call( + sal_Int32 nFunctionIndex, sal_Int32 nVtableOffset, + void ** gpreg, void ** fpreg, unsigned char * ovrflw, + void * pIndirectReturn, // AArch64 x8 indirect-result pointer (0 if none) + sal_uInt64 * pRegisterReturn /* space for register return */ ) +{ + // gpreg: this, [other gpr params x0..x7] + // fpreg: [fpr params d0..d7] + // ovrflw: [gpr or fpr params (properly aligned)] + // pIndirectReturn: x8 (the hidden return buffer), when bit 0x80000000 set. + // + // On AArch64 'this' is ALWAYS x0 = gpreg[0]; the hidden return pointer is + // the separate x8 register, not a displaced first GPR (unlike x86-64 SysV + // where it occupied gpreg[0] and 'this' moved to gpreg[1]). + if ( nFunctionIndex & 0x80000000 ) + nFunctionIndex &= 0x7fffffff; + + void * pThis = gpreg[0]; + pThis = static_cast<char *>( pThis ) - nVtableOffset; + + bridges::cpp_uno::shared::CppInterfaceProxy * pCppI = + bridges::cpp_uno::shared::CppInterfaceProxy::castInterfaceToProxy( pThis ); + + typelib_InterfaceTypeDescription * pTypeDescr = pCppI->getTypeDescr(); + + OSL_ENSURE( nFunctionIndex < pTypeDescr->nMapFunctionIndexToMemberIndex, "### illegal vtable index!\n" ); + if ( nFunctionIndex >= pTypeDescr->nMapFunctionIndexToMemberIndex ) + { + throw RuntimeException( OUString::createFromAscii("illegal vtable index!"), + reinterpret_cast<XInterface *>( pCppI ) ); + } + + // determine called method + sal_Int32 nMemberPos = pTypeDescr->pMapFunctionIndexToMemberIndex[nFunctionIndex]; + OSL_ENSURE( nMemberPos < pTypeDescr->nAllMembers, "### illegal member index!\n" ); + + TypeDescription aMemberDescr( pTypeDescr->ppAllMembers[nMemberPos] ); + + sal_uInt32 eRet; + switch ( aMemberDescr.get()->eTypeClass ) + { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + { + typelib_TypeDescriptionReference *pAttrTypeRef = + reinterpret_cast<typelib_InterfaceAttributeTypeDescription *>( aMemberDescr.get() )->pAttributeTypeRef; + + if ( pTypeDescr->pMapMemberIndexToFunctionIndex[nMemberPos] == nFunctionIndex ) + { + // is GET method + eRet = cpp2uno_call( pCppI, aMemberDescr.get(), pAttrTypeRef, + 0, 0, // no params + gpreg, fpreg, ovrflw, pIndirectReturn, pRegisterReturn ); + eRet = aarch64::get_return_kind( pAttrTypeRef ); + } + else + { + // is SET method + typelib_MethodParameter aParam; + aParam.pTypeRef = pAttrTypeRef; + aParam.bIn = sal_True; + aParam.bOut = sal_False; + + eRet = cpp2uno_call( pCppI, aMemberDescr.get(), + 0, // indicates void return + 1, &aParam, + gpreg, fpreg, ovrflw, pIndirectReturn, pRegisterReturn ); + } + break; + } + case typelib_TypeClass_INTERFACE_METHOD: + { + // is METHOD + switch ( nFunctionIndex ) + { + case 1: // acquire() + pCppI->acquireProxy(); // non virtual call! + eRet = typelib_TypeClass_VOID; + break; + case 2: // release() + pCppI->releaseProxy(); // non virtual call! + eRet = typelib_TypeClass_VOID; + break; + case 0: // queryInterface() opt + { + // queryInterface([in] type) returns an Any (> 16 bytes), + // so on AArch64 the result buffer is x8 (pIndirectReturn), + // 'this' is gpreg[0], and the type argument is the first + // real parameter, gpreg[1]. + typelib_TypeDescription * pTD = 0; + TYPELIB_DANGER_GET( &pTD, reinterpret_cast<Type *>( gpreg[1] )->getTypeLibType() ); + if ( pTD ) + { + XInterface * pInterface = 0; + (*pCppI->getBridge()->getCppEnv()->getRegisteredInterface) + ( pCppI->getBridge()->getCppEnv(), + reinterpret_cast<void **>(&pInterface), + pCppI->getOid().pData, + reinterpret_cast<typelib_InterfaceTypeDescription *>( pTD ) ); + + if ( pInterface ) + { + ::uno_any_construct( reinterpret_cast<uno_Any *>( pIndirectReturn ), + &pInterface, pTD, cpp_acquire ); + + pInterface->release(); + TYPELIB_DANGER_RELEASE( pTD ); + + reinterpret_cast<void **>( pRegisterReturn )[0] = pIndirectReturn; + eRet = typelib_TypeClass_ANY; + break; + } + TYPELIB_DANGER_RELEASE( pTD ); + } + } // else perform queryInterface() + default: + { + typelib_InterfaceMethodTypeDescription *pMethodTD = + reinterpret_cast<typelib_InterfaceMethodTypeDescription *>( aMemberDescr.get() ); + + eRet = cpp2uno_call( pCppI, aMemberDescr.get(), + pMethodTD->pReturnTypeRef, + pMethodTD->nParams, + pMethodTD->pParams, + gpreg, fpreg, ovrflw, pIndirectReturn, pRegisterReturn ); + eRet = aarch64::get_return_kind( pMethodTD->pReturnTypeRef ); + } + } + break; + } + default: + { + throw RuntimeException( OUString::createFromAscii("no member description found!"), + reinterpret_cast<XInterface *>( pCppI ) ); + // is here for dummy + eRet = typelib_TypeClass_VOID; + } + } + + return eRet; +} diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/except.cxx b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/except.cxx new file mode 100644 index 0000000000..e8caa2151c --- /dev/null +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/except.cxx @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_bridges.hxx" + +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) +#include <exception> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dlfcn.h> +#include <cxxabi.h> +#include <hash_map> +#include <sys/param.h> + +#include <rtl/strbuf.hxx> +#include <rtl/ustrbuf.hxx> +#include <osl/diagnose.h> +#include <osl/mutex.hxx> + +#include <com/sun/star/uno/genfunc.hxx> +#include "com/sun/star/uno/RuntimeException.hpp" +#include <typelib/typedescription.hxx> +#include <uno/any2.h> + +#include "share.hxx" + + +using namespace ::std; +using namespace ::osl; +using namespace ::rtl; +using namespace ::com::sun::star::uno; +using namespace ::__cxxabiv1; + + +namespace CPPU_CURRENT_NAMESPACE +{ + +namespace { + +typedef hash_map< void *, typelib_TypeDescription * > ThrownTypes; +typedef hash_map< OUString, type_info *, OUStringHash > ObservedRttiMap; + +ThrownTypes & thrownTypes() +{ + static ThrownTypes types; + return types; +} + +ObservedRttiMap & observedRttis() +{ + static ObservedRttiMap map; + return map; +} + +// Guards BOTH thrownTypes() and observedRttis(). Every access to either map +// must hold this one mutex; they are plain hash_maps, so an insertion racing a +// find or erase is undefined behaviour. RTTI::m_mutex may be held while +// acquiring this one (see RTTI::getRTTI), never the other way round. +Mutex & exceptionMapsMutex() +{ + static Mutex mutex; + return mutex; +} + +// libc++ marks a type_info whose object is not unique across images by setting +// the top bit of type_info::__type_name; comparison then falls back to strcmp +// of the mangled name (see __non_unique_arm_rtti_bit_impl in <typeinfo>). On +// arm64 Darwin clang emits the typeinfo of every keyless class -- which is every +// UNO exception -- hidden and therefore non-unique, so a synthesised object must +// set the bit too, or std::type_info::operator== degenerates to an address +// comparison and never matches the handler's real typeinfo. +sal_uIntPtr const NON_UNIQUE_RTTI_BIT = + static_cast< sal_uIntPtr >(1) << (8 * sizeof (sal_uIntPtr) - 1); + +RttiSiClassLayout const * siDonor() +{ + return reinterpret_cast< RttiSiClassLayout const * >( &typeid(RttiDonorDerived) ); +} +RttiClassLayout const * classDonor() +{ + return reinterpret_cast< RttiClassLayout const * >( &typeid(RttiDonorBase) ); +} + +// Refuse to synthesise unless the donors really have the layout we assume. +bool rttiDonorsUsable() +{ + return sizeof (void *) == 8 + && siDonor()->pBase == static_cast< void const * >( classDonor() ); +} + +// Mirror the platform's own convention rather than assuming it. +bool rttiIsNonUnique() +{ + return (siDonor()->nName & NON_UNIQUE_RTTI_BIT) != 0; +} + +} + +void dummy_can_throw_anything( char const * ) +{ +} + +//================================================================================================== +static OUString toUNOname( char const * p ) SAL_THROW( () ) +{ +#if OSL_DEBUG_LEVEL > 1 + char const * start = p; +#endif + + // example: N3com3sun4star4lang24IllegalArgumentExceptionE + + OUStringBuffer buf( 64 ); + OSL_ASSERT( 'N' == *p ); + ++p; // skip N + + while ('E' != *p) + { + // read chars count + long n = (*p++ - '0'); + while ('0' <= *p && '9' >= *p) + { + n *= 10; + n += (*p++ - '0'); + } + buf.appendAscii( p, n ); + p += n; + if ('E' != *p) + buf.append( (sal_Unicode)'.' ); + } + +#if OSL_DEBUG_LEVEL > 1 + OUString ret( buf.makeStringAndClear() ); + OString c_ret( OUStringToOString( ret, RTL_TEXTENCODING_ASCII_US ) ); + fprintf( stderr, "> toUNOname(): %s => %s\n", start, c_ret.getStr() ); + return ret; +#else + return buf.makeStringAndClear(); +#endif +} + +//================================================================================================== +static OString mangledRttiSymbol( OUString const & unoName ) SAL_THROW( () ) +{ + OStringBuffer buf( 64 ); + buf.append( RTL_CONSTASCII_STRINGPARAM("_ZTIN") ); + sal_Int32 index = 0; + do + { + OUString token( unoName.getToken( 0, '.', index ) ); + buf.append( token.getLength() ); + OString c_token( OUStringToOString( token, RTL_TEXTENCODING_ASCII_US ) ); + buf.append( c_token ); + } + while (index >= 0); + buf.append( 'E' ); + return buf.makeStringAndClear(); +} + +//================================================================================================== +class RTTI +{ + typedef hash_map< OUString, type_info *, OUStringHash > t_rtti_map; + + Mutex m_mutex; + t_rtti_map m_rttis; + t_rtti_map m_generatedRttis; + + type_info * synthesiseRTTI( + OString const & rSymbolName, + typelib_CompoundTypeDescription * pTypeDescr ) SAL_THROW( () ); + +public: + RTTI() SAL_THROW( () ); + ~RTTI() SAL_THROW( () ); + + type_info * getRTTI( typelib_CompoundTypeDescription * ) SAL_THROW( () ); +}; + +//__________________________________________________________________________________________________ +RTTI::RTTI() SAL_THROW( () ) +{ +} + +//__________________________________________________________________________________________________ +RTTI::~RTTI() SAL_THROW( () ) +{ +} + +//__________________________________________________________________________________________________ +type_info * RTTI::getRTTI( typelib_CompoundTypeDescription *pTypeDescr ) SAL_THROW( () ) +{ + OUString const & unoName = *(OUString const *)&pTypeDescr->aBase.pTypeName; + + // Recursive: synthesiseRTTI() re-enters getRTTI() for the base chain. + // osl::Mutex is a PTHREAD_MUTEX_RECURSIVE (sal/osl/unx/mutex.c), so this + // is safe. Lock order against exceptionMapsMutex() is unchanged. + MutexGuard guard( m_mutex ); + + { + MutexGuard observedGuard( exceptionMapsMutex() ); + ObservedRttiMap::const_iterator observed( observedRttis().find( unoName ) ); + if ( observed != observedRttis().end() ) + return observed->second; + } + + t_rtti_map::const_iterator it = m_rttis.find( unoName ); + if ( it != m_rttis.end() ) + return it->second; + + OString mangled = mangledRttiSymbol( unoName ); + + type_info * p = 0; + { + void * sym = dlsym( RTLD_DEFAULT, mangled.getStr() ); + if ( sym ) + p = *reinterpret_cast< type_info ** >( sym ); + } + + if ( p ) + { + m_rttis[ unoName ] = p; + return p; + } + + // Fallback: synthesise a type_info object. + OString symName = mangled; + p = synthesiseRTTI( symName, pTypeDescr ); + m_rttis[ unoName ] = p; + return p; +} + +} // namespace CPPU_CURRENT_NAMESPACE diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/makefile.mk b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/makefile.mk new file mode 100644 index 0000000000..e9b75c94fd --- /dev/null +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/makefile.mk @@ -0,0 +1,75 @@ +#************************************************************** +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +#************************************************************** + + +PRJ=..$/..$/.. + +PRJNAME=bridges +TARGET=$(COMNAME)_uno +LIBTARGET=no +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# --- Files -------------------------------------------------------- + +.IF "$(OS)-$(CPUNAME)-$(COMNAME)" == "FREEBSD-AARCH64-gcc3" + +.IF "$(cppu_no_leak)" == "" +CFLAGS += -DLEAK_STATIC_DATA +.ENDIF + +# In case someone enabled the non-standard -fomit-frame-pointer which does not +# work with the .cxx sources in this directory: +CFLAGSCXX += -fno-omit-frame-pointer + +SLOFILES= \ + $(SLO)$/abi.obj \ + $(SLO)$/except.obj \ + $(SLO)$/cpp2uno.obj \ + $(SLO)$/uno2cpp.obj \ + $(SLO)$/call.obj + +SHL1TARGET= $(TARGET) + +SHL1DEF=$(MISC)$/$(SHL1TARGET).def +SHL1IMPLIB=i$(TARGET) +SHL1VERSIONMAP=..$/..$/bridge_exports.map +SHL1RPATH=URELIB + +SHL1OBJS = $(SLOFILES) +SHL1LIBS = $(SLB)$/cpp_uno_shared.lib + +SHL1STDLIBS= \ + $(CPPULIB)\ + $(SALLIB) + +.ENDIF + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +# Assemble the AArch64 call trampoline (call.s) into call.obj. +$(SLO)$/%.obj: %.s + $(CXX) -c -o $(SLO)$/$(@:b).o $< -fPIC ; touch $@ diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/share.hxx b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/share.hxx new file mode 100644 index 0000000000..0b73a2a5fa --- /dev/null +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/share.hxx @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +#include "uno/mapping.h" + +#include <typeinfo> +#include <exception> +#include <cstddef> + +namespace CPPU_CURRENT_NAMESPACE +{ + +void dummy_can_throw_anything( char const * ); + +// Donor types for RTTI synthesis. Their type_info objects are emitted by the +// compiler, so they carry the real libc++abi vtables and the platform's own +// uniqueness convention. They must stay ordinary namespace-scope classes with +// no virtual functions and a single public non-virtual base -- exactly the +// shape of a generated UNO exception -- so that typeid(RttiDonorDerived) is a +// __si_class_type_info and typeid(RttiDonorBase) a __class_type_info. +// Do not move them into an anonymous namespace. +struct RttiDonorBase { sal_Int32 dummy; }; +struct RttiDonorDerived : public RttiDonorBase { sal_Int32 dummy2; }; + +// Itanium ABI object layouts (http://itanium-cxx-abi.github.io/cxx-abi/abi.html#rtti). +// libc++abi does not publish __cxxabiv1::__class_type_info, and declaring a +// look-alike class is not an option: it would get its own vtable, and +// __class_type_info::can_catch() dynamic_casts the thrown type to the real +// libc++abi class, so no typed handler would ever match. We therefore build +// raw storage in the ABI layout and install a borrowed, genuine vtable. +struct RttiClassLayout { void const * pVtable; sal_uIntPtr nName; }; +struct RttiSiClassLayout { void const * pVtable; sal_uIntPtr nName; void const * pBase; }; + +extern "C" void *__cxa_allocate_exception( + std::size_t thrown_size ) throw(); +extern "C" void __cxa_free_exception( void *thrown_exception ) throw(); +extern "C" void __cxa_throw ( + void *thrown_exception, std::type_info *tinfo, void (*dest) (void *) ) __attribute__((noreturn)); +extern "C" std::type_info *__cxa_current_exception_type(); + +//================================================================================================== +void raiseException( + uno_Any * pUnoExc, uno_Mapping * pUno2Cpp ); +//================================================================================================== +void fillUnoException( + std::type_info const & type, void * exception, uno_Any *, + uno_Mapping * pCpp2Uno ); +} diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/uno2cpp.cxx b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/uno2cpp.cxx new file mode 100644 index 0000000000..24e2627918 --- /dev/null +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/uno2cpp.cxx @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_bridges.hxx" + +#include <exception> +#include <typeinfo> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "rtl/alloc.h" +#include "rtl/ustrbuf.hxx" + +#include <com/sun/star/uno/genfunc.hxx> +#include "com/sun/star/uno/RuntimeException.hpp" +#include <uno/data.h> + +#include <bridges/cpp_uno/shared/bridge.hxx> +#include <bridges/cpp_uno/shared/types.hxx> +#include "bridges/cpp_uno/shared/unointerfaceproxy.hxx" +#include "bridges/cpp_uno/shared/vtables.hxx" + +#include "abi.hxx" +#include "share.hxx" + +using namespace ::rtl; +using namespace ::com::sun::star::uno; + +//================================================================================================== + +// The AArch64 outgoing-call trampoline, implemented in call.s. It loads the +// argument registers from the caller-prepared arrays, copies overflow args to +// the outgoing stack, performs the indirect call, and returns x0/x1 and d0..d3. +extern "C" void callVirtualFunction( + sal_uInt64 pFunction, sal_uInt64 pIndirectRet, + sal_uInt64 *pGPR, double *pFPR, + unsigned char *pStack, sal_uInt32 nStackBytes, + sal_uInt64 *pGPRReturn, double *pFPRReturn ); + +static void callVirtualMethod(void * pThis, sal_uInt32 nVtableIndex, + void * pRegisterReturn, typelib_TypeDescriptionReference * pReturnTypeRef, bool bSimpleReturn, + void * pIndirectReturn, + unsigned char *pStack, sal_uInt32 nStack, + sal_uInt64 *pGPR, sal_uInt32 nGPR, + double *pFPR, sal_uInt32 nFPR) __attribute__((noinline)); + +static void callVirtualMethod(void * pThis, sal_uInt32 nVtableIndex, + void * pRegisterReturn, typelib_TypeDescriptionReference * pReturnTypeRef, bool bSimpleReturn, + void * pIndirectReturn, + unsigned char *pStack, sal_uInt32 nStack, + sal_uInt64 *pGPR, sal_uInt32 nGPR, + double *pFPR, sal_uInt32 nFPR) +{ +#if OSL_DEBUG_LEVEL > 1 + // Let's figure out what is really going on here + { + fprintf( stderr, "= callVirtualMethod() =\nGPR's (%d): ", nGPR ); + for ( unsigned int i = 0; i < nGPR; ++i ) + fprintf( stderr, "0x%lx, ", pGPR[i] ); + fprintf( stderr, "\nFPR's (%d): ", nFPR ); + for ( unsigned int i = 0; i < nFPR; ++i ) + fprintf( stderr, "%f, ", pFPR[i] ); + // The overflow area is a packed byte image, not an array of words. + fprintf( stderr, "\nStack (%d bytes): ", nStack ); + for ( unsigned int i = 0; i < nStack; ++i ) + fprintf( stderr, "%02x ", pStack[i] ); + fprintf( stderr, "\n" ); + } +#endif + + // The call instruction within callVirtualFunction may throw exceptions. So + // that the compiler handles this correctly, it is important that (a) + // callVirtualMethod might call dummy_can_throw_anything (although this never + // happens at runtime), which in turn can throw exceptions, and (b) + // callVirtualMethod is not inlined at its call site (so that any exceptions + // thrown across the call are caught): + if ( !pThis ) + CPPU_CURRENT_NAMESPACE::dummy_can_throw_anything( "xxx" ); // address something + + // Should not happen, but... + if ( nFPR > aarch64::MAX_FPR_REGS ) + nFPR = aarch64::MAX_FPR_REGS; + if ( nGPR > aarch64::MAX_GPR_REGS ) + nGPR = aarch64::MAX_GPR_REGS; + + // Get pointer to the C++ virtual method from the vtable. + sal_uInt64 pMethod = *((sal_uInt64 *)pThis); + pMethod += 8 * nVtableIndex; + pMethod = *((sal_uInt64 *)pMethod); + + // Return register save areas: x0,x1 and d0..d3 (HFA up to 4 elements). + sal_uInt64 gpReturn[2] = { 0, 0 }; + double fpReturn[4] = { 0, 0, 0, 0 }; + + // Ensure the GPR/FPR arrays are the full register width even if fewer were + // filled (the trampoline always loads all 8 of each). + sal_uInt64 gpr[aarch64::MAX_GPR_REGS]; + double fpr[aarch64::MAX_FPR_REGS]; + for ( sal_uInt32 i = 0; i < aarch64::MAX_GPR_REGS; ++i ) + gpr[i] = ( i < nGPR ) ? pGPR[i] : 0; + for ( sal_uInt32 i = 0; i < aarch64::MAX_FPR_REGS; ++i ) + fpr[i] = ( i < nFPR ) ? pFPR[i] : 0; + + callVirtualFunction( + pMethod, + reinterpret_cast<sal_uInt64>( pIndirectReturn ), // x8, 0 if none + gpr, fpr, + pStack, nStack, + gpReturn, fpReturn ); + + switch (pReturnTypeRef->eTypeClass) + { + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + *reinterpret_cast<sal_uInt64 *>( pRegisterReturn ) = gpReturn[0]; + break; + case typelib_TypeClass_LONG: + *reinterpret_cast<sal_Int32 *>( pRegisterReturn ) = + *reinterpret_cast<sal_Int32 *>( &gpReturn[0] ); + break; + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_ENUM: + *reinterpret_cast<sal_uInt32 *>( pRegisterReturn ) = + *reinterpret_cast<sal_uInt32 *>( &gpReturn[0] ); + break; + case typelib_TypeClass_CHAR: + case typelib_TypeClass_UNSIGNED_SHORT: + *reinterpret_cast<sal_uInt16 *>( pRegisterReturn ) = *reinterpret_cast<sal_uInt16*>( &gpReturn[0] ); + break; + case typelib_TypeClass_SHORT: + *reinterpret_cast<sal_Int16 *>( pRegisterReturn ) = + *reinterpret_cast<sal_Int16 *>( &gpReturn[0] ); + break; + case typelib_TypeClass_BOOLEAN: + *reinterpret_cast<sal_uInt8 *>( pRegisterReturn ) = *reinterpret_cast<sal_uInt8*>( &gpReturn[0] ); + break; + case typelib_TypeClass_BYTE: + *reinterpret_cast<sal_Int8 *>( pRegisterReturn ) = + *reinterpret_cast<sal_Int8 *>( &gpReturn[0] ); + break; + case typelib_TypeClass_FLOAT: + *reinterpret_cast<float *>( pRegisterReturn ) = + *reinterpret_cast<float *>( &fpReturn[0] ); + break; + case typelib_TypeClass_DOUBLE: + *reinterpret_cast<double *>( pRegisterReturn ) = + *reinterpret_cast<double *>( &fpReturn[0] ); + break; + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + aarch64::fill_struct( pReturnTypeRef, gpReturn, fpReturn, pRegisterReturn ); + break; + default: + break; + } +} + +//================================================================================================== +// The AArch64 outgoing-call trampoline, implemented in call.s, is declared in ABI +// and used by callVirtualMethod above. The rest of the file implements the +// public callVirtualMethod entrypoints used by the bridge; these are identical +// to the macOS AArch64 implementation and therefore are compatible on FreeBSD. + +extern "C" void callVirtualFunction( + sal_uInt64 pFunction, sal_uInt64 pIndirectRet, + sal_uInt64 *pGPR, double *pFPR, + unsigned char *pStack, sal_uInt32 nStackBytes, + sal_uInt64 *pGPRReturn, double *pFPRReturn ); + +// ... per-bridge wrappers are generated at build time; no further code required here.
