xref: /trunk/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/abi.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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 #include <string.h>
53 
54 using namespace aarch64;
55 
56 namespace {
57 
58 // The element type of a Homogeneous Floating-point Aggregate.
59 enum HfaKind
60 {
61     HFA_NONE,       // not (yet) an HFA
62     HFA_FLOAT,      // all members are FLOAT (4-byte)
63     HFA_DOUBLE      // all members are DOUBLE (8-byte)
64 };
65 
66 // Combine the running HFA kind with a newly-seen member kind.  Two members
67 // of different FP types, or any non-FP member, break the homogeneity.
68 HfaKind mergeHfa( HfaKind running, HfaKind seen )
69 {
70     if ( seen == HFA_NONE )
71         return HFA_NONE;
72     if ( running == HFA_NONE )
73         return seen;
74     return ( running == seen ) ? running : HFA_NONE;
75 }
76 
77 bool isComplexAggregate( typelib_TypeDescriptionReference *pTypeRef )
78 {
79     typelib_TypeDescription * pTypeDescr = 0;
80     TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
81     const typelib_CompoundTypeDescription *pComp =
82         reinterpret_cast<const typelib_CompoundTypeDescription *>( pTypeDescr );
83     bool complex = pComp->pBaseTypeDescription != 0 &&
84         isComplexAggregate( pComp->pBaseTypeDescription->aBase.pWeakRef );
85     for ( sal_Int32 i = 0; !complex && i < pComp->nMembers; ++i )
86     {
87         typelib_TypeClass typeClass = pComp->ppTypeRefs[i]->eTypeClass;
88         if ( typeClass == typelib_TypeClass_STRUCT ||
89              typeClass == typelib_TypeClass_EXCEPTION )
90             complex = isComplexAggregate( pComp->ppTypeRefs[i] );
91         else
92             complex = !bridges::cpp_uno::shared::isSimpleType( typeClass );
93     }
94     TYPELIB_DANGER_RELEASE( pTypeDescr );
95     return complex;
96 }
97 
98 // Recursively determine whether pTypeRef is (part of) a homogeneous
99 // floating-point aggregate, accumulating the element kind and member count.
100 //
101 // Returns false the moment homogeneity is violated (a non-FP scalar, or a
102 // second distinct FP type, or > 4 elements).  A FLOAT/DOUBLE scalar counts as
103 // a 1-element HFA of itself; a struct flattens its members (and base classes).
104 bool collectHfa( typelib_TypeDescriptionReference *pTypeRef, HfaKind &rKind, int &rCount )
105 {
106     switch ( pTypeRef->eTypeClass )
107     {
108         case typelib_TypeClass_FLOAT:
109             rKind = mergeHfa( rKind, HFA_FLOAT );
110             if ( rKind == HFA_NONE ) return false;
111             return ( ++rCount <= 4 );
112 
113         case typelib_TypeClass_DOUBLE:
114             rKind = mergeHfa( rKind, HFA_DOUBLE );
115             if ( rKind == HFA_NONE ) return false;
116             return ( ++rCount <= 4 );
117 
118         case typelib_TypeClass_STRUCT:
119         case typelib_TypeClass_EXCEPTION:
120         {
121             typelib_TypeDescription * pTypeDescr = 0;
122             TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
123 
124             const typelib_CompoundTypeDescription *pComp =
125                 reinterpret_cast<const typelib_CompoundTypeDescription*>( pTypeDescr );
126 
127             // rCount is cumulative over the whole recursion, so remember where
128             // this aggregate started in order to size-check it below.
129             const int nCountAtEntry = rCount;
130             bool bOk = true;
131 
132             // Flatten base class first (its members precede ours in layout).
133             if ( pComp->pBaseTypeDescription )
134             {
135                 bOk = collectHfa(
136                     pComp->pBaseTypeDescription->aBase.pWeakRef, rKind, rCount );
137             }
138 
139             for ( sal_Int32 i = 0; bOk && i < pComp->nMembers; ++i )
140                 bOk = collectHfa( pComp->ppTypeRefs[i], rKind, rCount );
141 
142             if ( bOk )
143             {
144                 // Reject anything the elements do not tile exactly: only the
145                 // elements contributed by THIS aggregate count towards its size.
146                 sal_Int32 elementSize = rKind == HFA_FLOAT ? 4 : 8;
147                 bOk = pTypeDescr->nSize ==
148                     ( rCount - nCountAtEntry ) * elementSize;
149                 for ( sal_Int32 i = 0; bOk && i < pComp->nMembers; ++i )
150                     bOk = pComp->pMemberOffsets[i] % elementSize == 0;
151             }
152 
153             TYPELIB_DANGER_RELEASE( pTypeDescr );
154             return bOk;
155         }
156 
157         default:
158             // Any non-FP, non-aggregate member breaks homogeneity.
159             rKind = HFA_NONE;
160             return false;
161     }
162 }
163 
164 // Classify an aggregate (STRUCT/EXCEPTION).  Sets the GPR/FPR counts and
165 // returns true if it is passed in registers, false if it must be passed
166 // indirectly (in memory).
167 bool classifyAggregate( typelib_TypeDescriptionReference *pTypeRef, int &nUsedGPR, int &nUsedFPR )
168 {
169     // First, the HFA test.
170     HfaKind kind = HFA_NONE;
171     int count = 0;
172     if ( collectHfa( pTypeRef, kind, count ) && kind != HFA_NONE && count >= 1 && count <= 4 )
173     {
174         nUsedGPR = 0;
175         nUsedFPR = count;       // one v register per member
176         return true;
177     }
178 
179     // Otherwise classify by size.
180     typelib_TypeDescription * pTypeDescr = 0;
181     TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
182     sal_Int32 nSize = pTypeDescr->nSize;
183     TYPELIB_DANGER_RELEASE( pTypeDescr );
184 
185     if ( nSize > 16 )
186     {
187         // Non-HFA aggregate > 16 bytes => passed indirectly.
188         return false;
189     }
190 
191     // Non-HFA aggregate <= 16 bytes => 1 or 2 GPRs (8 bytes each).
192     nUsedGPR = ( nSize > 8 ) ? 2 : 1;
193     nUsedFPR = 0;
194     return true;
195 }
196 
197 } // anonymous namespace
198 
199 bool aarch64::examine_argument( typelib_TypeDescriptionReference *pTypeRef, bool bInReturn, int &nUsedGPR, int &nUsedFPR )
200 {
201     nUsedGPR = 0;
202     nUsedFPR = 0;
203 
204     switch ( pTypeRef->eTypeClass )
205     {
206         case typelib_TypeClass_VOID:
207             return true;
208 
209         case typelib_TypeClass_CHAR:
210         case typelib_TypeClass_BOOLEAN:
211         case typelib_TypeClass_BYTE:
212         case typelib_TypeClass_SHORT:
213         case typelib_TypeClass_UNSIGNED_SHORT:
214         case typelib_TypeClass_LONG:
215         case typelib_TypeClass_UNSIGNED_LONG:
216         case typelib_TypeClass_HYPER:
217         case typelib_TypeClass_UNSIGNED_HYPER:
218         case typelib_TypeClass_ENUM:
219             nUsedGPR = 1;
220             return true;
221 
222         case typelib_TypeClass_FLOAT:
223         case typelib_TypeClass_DOUBLE:
224             nUsedFPR = 1;
225             return true;
226 
227         // These UNO types are always handled by the bridge as a pointer/
228         // reference (one GPR), never passed by value through this classifier.
229         case typelib_TypeClass_STRING:
230         case typelib_TypeClass_TYPE:
231         case typelib_TypeClass_ANY:
232         case typelib_TypeClass_TYPEDEF:
233         case typelib_TypeClass_SEQUENCE:
234         case typelib_TypeClass_INTERFACE:
235             nUsedGPR = 1;
236             return true;
237 
238         case typelib_TypeClass_STRUCT:
239         case typelib_TypeClass_EXCEPTION:
240             if ( bInReturn )
241                 return classifyAggregate( pTypeRef, nUsedGPR, nUsedFPR );
242             nUsedGPR = 1; // generated UNO C++ bindings pass aggregates by const reference
243             return true;
244 
245         default:
246 #if OSL_DEBUG_LEVEL > 1
247             OSL_TRACE( "Unhandled case: pTypeRef->eTypeClass == %d\n", pTypeRef->eTypeClass );
248 #endif
249             OSL_ASSERT( 0 );
250     }
251     return false;
252 }
253 
254 bool aarch64::return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef )
255 {
256     switch ( pTypeRef->eTypeClass )
257     {
258         case typelib_TypeClass_STRING:
259         case typelib_TypeClass_TYPE:
260         case typelib_TypeClass_ANY:
261         case typelib_TypeClass_TYPEDEF:
262         case typelib_TypeClass_UNION:
263         case typelib_TypeClass_ARRAY:
264         case typelib_TypeClass_SEQUENCE:
265         case typelib_TypeClass_INTERFACE:
266             // These are C++ wrapper objects, not pointer-sized scalar values.
267             // Apple's arm64 C++ ABI returns them through the buffer in x8.
268             return true;
269         default:
270             break;
271     }
272 
273     if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT ||
274          pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION )
275     {
276         if ( isComplexAggregate( pTypeRef ) )
277             return true;
278     }
279 
280     int g, s;
281     // Returned in registers iff examine_argument() says it fits; otherwise the
282     // caller must pass an indirect-result buffer in x8.
283     return !examine_argument( pTypeRef, true, g, s );
284 }
285 
286 sal_uInt32 aarch64::get_return_kind( typelib_TypeDescriptionReference *pTypeRef )
287 {
288     if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT ||
289          pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION )
290     {
291         HfaKind kind = HFA_NONE;
292         int count = 0;
293         if ( collectHfa( pTypeRef, kind, count ) && count >= 1 && count <= 4 )
294             return kind == HFA_FLOAT ? RETURN_KIND_HFA_FLOAT : RETURN_KIND_HFA_DOUBLE;
295     }
296     return pTypeRef->eTypeClass;
297 }
298 
299 void aarch64::fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal_uInt64 *pGPR, const double *pFPR, void *pStruct )
300 {
301     int nUsedGPR = 0;
302     int nUsedFPR = 0;
303     if ( !examine_argument( pTypeRef, true, nUsedGPR, nUsedFPR ) )
304     {
305         // Should not happen: indirect returns are written through x8 directly,
306         // not scattered here.
307         OSL_ASSERT( 0 );
308         return;
309     }
310 
311     if ( nUsedFPR > 0 )
312     {
313         // HFA: each member occupies one v register; the members are contiguous
314         // in the struct.  Copy element-by-element to honour FLOAT (4-byte) vs
315         // DOUBLE (8-byte) element width.
316         HfaKind kind = HFA_NONE;
317         int count = 0;
318         collectHfa( pTypeRef, kind, count );
319         if ( kind == HFA_FLOAT )
320         {
321             float *pDest = reinterpret_cast<float *>( pStruct );
322             for ( int i = 0; i < nUsedFPR; ++i )
323                 pDest[i] = *reinterpret_cast<const float *>( pFPR + i );
324         }
325         else // HFA_DOUBLE
326         {
327             double *pDest = reinterpret_cast<double *>( pStruct );
328             for ( int i = 0; i < nUsedFPR; ++i )
329                 pDest[i] = pFPR[i];
330         }
331     }
332     else
333     {
334         typelib_TypeDescription * pTypeDescr = 0;
335         TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
336         // The registers contain up to 16 bytes, but the destination has the
337         // aggregate's exact size and need not be 64-bit aligned.
338         memcpy( pStruct, pGPR, pTypeDescr->nSize );
339         TYPELIB_DANGER_RELEASE( pTypeDescr );
340     }
341 }
342 
343 sal_uInt32 aarch64::align_stack_offset(
344     sal_uInt32 offset, typelib_TypeDescriptionReference *pTypeRef )
345 {
346     typelib_TypeDescription * pTypeDescr = 0;
347     TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
348     sal_uInt32 alignment = pTypeDescr->nAlignment;
349     TYPELIB_DANGER_RELEASE( pTypeDescr );
350     if ( alignment == 0 )
351         alignment = 1;
352     return (offset + alignment - 1) & ~(alignment - 1);
353 }
354 
355 sal_uInt32 aarch64::stack_size( typelib_TypeDescriptionReference *pTypeRef )
356 {
357     typelib_TypeDescription * pTypeDescr = 0;
358     TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
359     sal_uInt32 size = pTypeDescr->nSize;
360     TYPELIB_DANGER_RELEASE( pTypeDescr );
361     return size;
362 }
363