xref: /trunk/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/abi.cxx (revision a23f5aaf7731494d1484f0272a1646643fd459f5)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_bridges.hxx"
26 
27 // This is an implementation of the parameter-classification rules of the
28 // AArch64 procedure call standard ("Procedure Call Standard for the Arm 64-bit
29 // Architecture", ARM IHI 0055), with the deviations documented in Apple's
30 // "Writing ARM64 Code for Apple Platforms".
31 //
32 // Unlike the System V AMD64 ABI (used by the x86-64 bridge), AAPCS64 does not
33 // split aggregates into per-eightbyte INTEGER/SSE classes.  Instead:
34 //   * scalars go in one GPR (x) or one FP/SIMD (v) register;
35 //   * a Homogeneous Floating-point Aggregate (HFA: <= 4 members, all the same
36 //     FP type, recursively) goes in consecutive v registers;
37 //   * any other aggregate <= 16 bytes goes in 1-2 GPRs;
38 //   * a non-HFA aggregate > 16 bytes is passed indirectly (a pointer to a
39 //     caller-allocated copy).
40 // Register fill is "all or nothing": if an aggregate does not fit entirely in
41 // the remaining registers of its bank, it is passed wholly on the stack.
42 //
43 // This is a clean-room implementation from the public specifications; see
44 // ../../../../AAPCS64_BRIDGE_SPEC.md.  libffi's aarch64 backend was consulted
45 // only as a behavioural reference; no code is copied.
46 
47 #include "abi.hxx"
48 
49 #include "bridges/cpp_uno/shared/types.hxx"
50 
51 #include <rtl/ustring.hxx>
52 
53 using namespace aarch64;
54 
55 namespace {
56 
57 // The element type of a Homogeneous Floating-point Aggregate.
58 enum HfaKind
59 {
60     HFA_NONE,       // not (yet) an HFA
61     HFA_FLOAT,      // all members are FLOAT (4-byte)
62     HFA_DOUBLE      // all members are DOUBLE (8-byte)
63 };
64 
65 // Combine the running HFA kind with a newly-seen member kind.  Two members
66 // of different FP types, or any non-FP member, break the homogeneity.
67 HfaKind mergeHfa( HfaKind running, HfaKind seen )
68 {
69     if ( seen == HFA_NONE )
70         return HFA_NONE;
71     if ( running == HFA_NONE )
72         return seen;
73     return ( running == seen ) ? running : HFA_NONE;
74 }
75 
76 bool isComplexAggregate( typelib_TypeDescriptionReference *pTypeRef )
77 {
78     typelib_TypeDescription * pTypeDescr = 0;
79     TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
80     const typelib_CompoundTypeDescription *pComp =
81         reinterpret_cast<const typelib_CompoundTypeDescription *>( pTypeDescr );
82     bool complex = pComp->pBaseTypeDescription != 0 &&
83         isComplexAggregate( pComp->pBaseTypeDescription->aBase.pWeakRef );
84     for ( sal_Int32 i = 0; !complex && i < pComp->nMembers; ++i )
85     {
86         typelib_TypeClass typeClass = pComp->ppTypeRefs[i]->eTypeClass;
87         if ( typeClass == typelib_TypeClass_STRUCT ||
88              typeClass == typelib_TypeClass_EXCEPTION )
89             complex = isComplexAggregate( pComp->ppTypeRefs[i] );
90         else
91             complex = !bridges::cpp_uno::shared::isSimpleType( typeClass );
92     }
93     TYPELIB_DANGER_RELEASE( pTypeDescr );
94     return complex;
95 }
96 
97 // Recursively determine whether pTypeRef is (part of) a homogeneous
98 // floating-point aggregate, accumulating the element kind and member count.
99 //
100 // Returns false the moment homogeneity is violated (a non-FP scalar, or a
101 // second distinct FP type, or > 4 elements).  A FLOAT/DOUBLE scalar counts as
102 // a 1-element HFA of itself; a struct flattens its members (and base classes).
103 bool collectHfa( typelib_TypeDescriptionReference *pTypeRef, HfaKind &rKind, int &rCount )
104 {
105     switch ( pTypeRef->eTypeClass )
106     {
107         case typelib_TypeClass_FLOAT:
108             rKind = mergeHfa( rKind, HFA_FLOAT );
109             if ( rKind == HFA_NONE ) return false;
110             return ( ++rCount <= 4 );
111 
112         case typelib_TypeClass_DOUBLE:
113             rKind = mergeHfa( rKind, HFA_DOUBLE );
114             if ( rKind == HFA_NONE ) return false;
115             return ( ++rCount <= 4 );
116 
117         case typelib_TypeClass_STRUCT:
118         case typelib_TypeClass_EXCEPTION:
119         {
120             typelib_TypeDescription * pTypeDescr = 0;
121             TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
122 
123             const typelib_CompoundTypeDescription *pComp =
124                 reinterpret_cast<const typelib_CompoundTypeDescription*>( pTypeDescr );
125 
126             bool bOk = true;
127 
128             // Flatten base class first (its members precede ours in layout).
129             if ( pComp->pBaseTypeDescription )
130             {
131                 bOk = collectHfa(
132                     pComp->pBaseTypeDescription->aBase.pWeakRef, rKind, rCount );
133             }
134 
135             for ( sal_Int32 i = 0; bOk && i < pComp->nMembers; ++i )
136                 bOk = collectHfa( pComp->ppTypeRefs[i], rKind, rCount );
137 
138             TYPELIB_DANGER_RELEASE( pTypeDescr );
139             return bOk;
140         }
141 
142         default:
143             // Any non-FP, non-aggregate member breaks homogeneity.
144             rKind = HFA_NONE;
145             return false;
146     }
147 }
148 
149 // Classify an aggregate (STRUCT/EXCEPTION).  Sets the GPR/FPR counts and
150 // returns true if it is passed in registers, false if it must be passed
151 // indirectly (in memory).
152 bool classifyAggregate( typelib_TypeDescriptionReference *pTypeRef, int &nUsedGPR, int &nUsedFPR )
153 {
154     // First, the HFA test.
155     HfaKind kind = HFA_NONE;
156     int count = 0;
157     if ( collectHfa( pTypeRef, kind, count ) && kind != HFA_NONE && count >= 1 && count <= 4 )
158     {
159         nUsedGPR = 0;
160         nUsedFPR = count;       // one v register per member
161         return true;
162     }
163 
164     // Otherwise classify by size.
165     typelib_TypeDescription * pTypeDescr = 0;
166     TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
167     sal_Int32 nSize = pTypeDescr->nSize;
168     TYPELIB_DANGER_RELEASE( pTypeDescr );
169 
170     if ( nSize > 16 )
171     {
172         // Non-HFA aggregate > 16 bytes => passed indirectly.
173         return false;
174     }
175 
176     // Non-HFA aggregate <= 16 bytes => 1 or 2 GPRs (8 bytes each).
177     nUsedGPR = ( nSize > 8 ) ? 2 : 1;
178     nUsedFPR = 0;
179     return true;
180 }
181 
182 } // anonymous namespace
183 
184 bool aarch64::examine_argument( typelib_TypeDescriptionReference *pTypeRef, bool bInReturn, int &nUsedGPR, int &nUsedFPR )
185 {
186     nUsedGPR = 0;
187     nUsedFPR = 0;
188 
189     switch ( pTypeRef->eTypeClass )
190     {
191         case typelib_TypeClass_VOID:
192             return true;
193 
194         case typelib_TypeClass_CHAR:
195         case typelib_TypeClass_BOOLEAN:
196         case typelib_TypeClass_BYTE:
197         case typelib_TypeClass_SHORT:
198         case typelib_TypeClass_UNSIGNED_SHORT:
199         case typelib_TypeClass_LONG:
200         case typelib_TypeClass_UNSIGNED_LONG:
201         case typelib_TypeClass_HYPER:
202         case typelib_TypeClass_UNSIGNED_HYPER:
203         case typelib_TypeClass_ENUM:
204             nUsedGPR = 1;
205             return true;
206 
207         case typelib_TypeClass_FLOAT:
208         case typelib_TypeClass_DOUBLE:
209             nUsedFPR = 1;
210             return true;
211 
212         // These UNO types are always handled by the bridge as a pointer/
213         // reference (one GPR), never passed by value through this classifier.
214         case typelib_TypeClass_STRING:
215         case typelib_TypeClass_TYPE:
216         case typelib_TypeClass_ANY:
217         case typelib_TypeClass_TYPEDEF:
218         case typelib_TypeClass_SEQUENCE:
219         case typelib_TypeClass_INTERFACE:
220             nUsedGPR = 1;
221             return true;
222 
223         case typelib_TypeClass_STRUCT:
224         case typelib_TypeClass_EXCEPTION:
225             if ( bInReturn )
226                 return classifyAggregate( pTypeRef, nUsedGPR, nUsedFPR );
227             nUsedGPR = 1; // generated UNO C++ bindings pass aggregates by const reference
228             return true;
229 
230         default:
231 #if OSL_DEBUG_LEVEL > 1
232             OSL_TRACE( "Unhandled case: pTypeRef->eTypeClass == %d\n", pTypeRef->eTypeClass );
233 #endif
234             OSL_ASSERT( 0 );
235     }
236     return false;
237 }
238 
239 bool aarch64::return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef )
240 {
241     switch ( pTypeRef->eTypeClass )
242     {
243         case typelib_TypeClass_STRING:
244         case typelib_TypeClass_TYPE:
245         case typelib_TypeClass_ANY:
246         case typelib_TypeClass_TYPEDEF:
247         case typelib_TypeClass_UNION:
248         case typelib_TypeClass_ARRAY:
249         case typelib_TypeClass_SEQUENCE:
250         case typelib_TypeClass_INTERFACE:
251             // These are C++ wrapper objects, not pointer-sized scalar values.
252             // Apple's arm64 C++ ABI returns them through the buffer in x8.
253             return true;
254         default:
255             break;
256     }
257 
258     if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT ||
259          pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION )
260     {
261         if ( isComplexAggregate( pTypeRef ) )
262             return true;
263     }
264 
265     int g, s;
266     // Returned in registers iff examine_argument() says it fits; otherwise the
267     // caller must pass an indirect-result buffer in x8.
268     return !examine_argument( pTypeRef, true, g, s );
269 }
270 
271 sal_uInt32 aarch64::get_return_kind( typelib_TypeDescriptionReference *pTypeRef )
272 {
273     if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT ||
274          pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION )
275     {
276         HfaKind kind = HFA_NONE;
277         int count = 0;
278         if ( collectHfa( pTypeRef, kind, count ) && count >= 1 && count <= 4 )
279             return kind == HFA_FLOAT ? RETURN_KIND_HFA_FLOAT : RETURN_KIND_HFA_DOUBLE;
280     }
281     return pTypeRef->eTypeClass;
282 }
283 
284 void aarch64::fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal_uInt64 *pGPR, const double *pFPR, void *pStruct )
285 {
286     int nUsedGPR = 0;
287     int nUsedFPR = 0;
288     if ( !examine_argument( pTypeRef, true, nUsedGPR, nUsedFPR ) )
289     {
290         // Should not happen: indirect returns are written through x8 directly,
291         // not scattered here.
292         OSL_ASSERT( 0 );
293         return;
294     }
295 
296     if ( nUsedFPR > 0 )
297     {
298         // HFA: each member occupies one v register; the members are contiguous
299         // in the struct.  Copy element-by-element to honour FLOAT (4-byte) vs
300         // DOUBLE (8-byte) element width.
301         HfaKind kind = HFA_NONE;
302         int count = 0;
303         collectHfa( pTypeRef, kind, count );
304         if ( kind == HFA_FLOAT )
305         {
306             float *pDest = reinterpret_cast<float *>( pStruct );
307             for ( int i = 0; i < nUsedFPR; ++i )
308                 pDest[i] = *reinterpret_cast<const float *>( pFPR + i );
309         }
310         else // HFA_DOUBLE
311         {
312             double *pDest = reinterpret_cast<double *>( pStruct );
313             for ( int i = 0; i < nUsedFPR; ++i )
314                 pDest[i] = pFPR[i];
315         }
316     }
317     else
318     {
319         // Non-HFA aggregate <= 16 bytes: raw copy of the 1-2 GPRs.
320         sal_uInt64 *pDest = reinterpret_cast<sal_uInt64 *>( pStruct );
321         for ( int i = 0; i < nUsedGPR; ++i )
322             pDest[i] = pGPR[i];
323     }
324 }
325