xref: /trunk/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/abi.cxx (revision 5e139d9fe42a654147771da4118aea6285c03168)
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 <rtl/ustring.hxx>
50 
51 using namespace aarch64;
52 
53 namespace {
54 
55 // The element type of a Homogeneous Floating-point Aggregate.
56 enum HfaKind
57 {
58     HFA_NONE,       // not (yet) an HFA
59     HFA_FLOAT,      // all members are FLOAT (4-byte)
60     HFA_DOUBLE      // all members are DOUBLE (8-byte)
61 };
62 
63 // Combine the running HFA kind with a newly-seen member kind.  Two members
64 // of different FP types, or any non-FP member, break the homogeneity.
mergeHfa(HfaKind running,HfaKind seen)65 HfaKind mergeHfa( HfaKind running, HfaKind seen )
66 {
67     if ( seen == HFA_NONE )
68         return HFA_NONE;
69     if ( running == HFA_NONE )
70         return seen;
71     return ( running == seen ) ? running : HFA_NONE;
72 }
73 
74 // Recursively determine whether pTypeRef is (part of) a homogeneous
75 // floating-point aggregate, accumulating the element kind and member count.
76 //
77 // Returns false the moment homogeneity is violated (a non-FP scalar, or a
78 // second distinct FP type, or > 4 elements).  A FLOAT/DOUBLE scalar counts as
79 // a 1-element HFA of itself; a struct flattens its members (and base classes).
collectHfa(typelib_TypeDescriptionReference * pTypeRef,HfaKind & rKind,int & rCount)80 bool collectHfa( typelib_TypeDescriptionReference *pTypeRef, HfaKind &rKind, int &rCount )
81 {
82     switch ( pTypeRef->eTypeClass )
83     {
84         case typelib_TypeClass_FLOAT:
85             rKind = mergeHfa( rKind, HFA_FLOAT );
86             if ( rKind == HFA_NONE ) return false;
87             return ( ++rCount <= 4 );
88 
89         case typelib_TypeClass_DOUBLE:
90             rKind = mergeHfa( rKind, HFA_DOUBLE );
91             if ( rKind == HFA_NONE ) return false;
92             return ( ++rCount <= 4 );
93 
94         case typelib_TypeClass_STRUCT:
95         case typelib_TypeClass_EXCEPTION:
96         {
97             typelib_TypeDescription * pTypeDescr = 0;
98             TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
99 
100             const typelib_CompoundTypeDescription *pComp =
101                 reinterpret_cast<const typelib_CompoundTypeDescription*>( pTypeDescr );
102 
103             bool bOk = true;
104 
105             // Flatten base class first (its members precede ours in layout).
106             if ( pComp->pBaseTypeDescription )
107             {
108                 bOk = collectHfa(
109                     pComp->pBaseTypeDescription->aBase.pWeakRef, rKind, rCount );
110             }
111 
112             for ( sal_Int32 i = 0; bOk && i < pComp->nMembers; ++i )
113                 bOk = collectHfa( pComp->ppTypeRefs[i], rKind, rCount );
114 
115             TYPELIB_DANGER_RELEASE( pTypeDescr );
116             return bOk;
117         }
118 
119         default:
120             // Any non-FP, non-aggregate member breaks homogeneity.
121             rKind = HFA_NONE;
122             return false;
123     }
124 }
125 
126 // Classify an aggregate (STRUCT/EXCEPTION).  Sets the GPR/FPR counts and
127 // returns true if it is passed in registers, false if it must be passed
128 // indirectly (in memory).
classifyAggregate(typelib_TypeDescriptionReference * pTypeRef,int & nUsedGPR,int & nUsedFPR)129 bool classifyAggregate( typelib_TypeDescriptionReference *pTypeRef, int &nUsedGPR, int &nUsedFPR )
130 {
131     // First, the HFA test.
132     HfaKind kind = HFA_NONE;
133     int count = 0;
134     if ( collectHfa( pTypeRef, kind, count ) && kind != HFA_NONE && count >= 1 && count <= 4 )
135     {
136         nUsedGPR = 0;
137         nUsedFPR = count;       // one v register per member
138         return true;
139     }
140 
141     // Otherwise classify by size.
142     typelib_TypeDescription * pTypeDescr = 0;
143     TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
144     sal_Int32 nSize = pTypeDescr->nSize;
145     TYPELIB_DANGER_RELEASE( pTypeDescr );
146 
147     if ( nSize > 16 )
148     {
149         // Non-HFA aggregate > 16 bytes => passed indirectly.
150         return false;
151     }
152 
153     // Non-HFA aggregate <= 16 bytes => 1 or 2 GPRs (8 bytes each).
154     nUsedGPR = ( nSize > 8 ) ? 2 : 1;
155     nUsedFPR = 0;
156     return true;
157 }
158 
159 } // anonymous namespace
160 
examine_argument(typelib_TypeDescriptionReference * pTypeRef,bool,int & nUsedGPR,int & nUsedFPR)161 bool aarch64::examine_argument( typelib_TypeDescriptionReference *pTypeRef, bool /*bInReturn*/, int &nUsedGPR, int &nUsedFPR )
162 {
163     nUsedGPR = 0;
164     nUsedFPR = 0;
165 
166     switch ( pTypeRef->eTypeClass )
167     {
168         case typelib_TypeClass_VOID:
169             return true;
170 
171         case typelib_TypeClass_CHAR:
172         case typelib_TypeClass_BOOLEAN:
173         case typelib_TypeClass_BYTE:
174         case typelib_TypeClass_SHORT:
175         case typelib_TypeClass_UNSIGNED_SHORT:
176         case typelib_TypeClass_LONG:
177         case typelib_TypeClass_UNSIGNED_LONG:
178         case typelib_TypeClass_HYPER:
179         case typelib_TypeClass_UNSIGNED_HYPER:
180         case typelib_TypeClass_ENUM:
181             nUsedGPR = 1;
182             return true;
183 
184         case typelib_TypeClass_FLOAT:
185         case typelib_TypeClass_DOUBLE:
186             nUsedFPR = 1;
187             return true;
188 
189         // These UNO types are always handled by the bridge as a pointer/
190         // reference (one GPR), never passed by value through this classifier.
191         case typelib_TypeClass_STRING:
192         case typelib_TypeClass_TYPE:
193         case typelib_TypeClass_ANY:
194         case typelib_TypeClass_TYPEDEF:
195         case typelib_TypeClass_SEQUENCE:
196         case typelib_TypeClass_INTERFACE:
197             nUsedGPR = 1;
198             return true;
199 
200         case typelib_TypeClass_STRUCT:
201         case typelib_TypeClass_EXCEPTION:
202             return classifyAggregate( pTypeRef, nUsedGPR, nUsedFPR );
203 
204         default:
205 #if OSL_DEBUG_LEVEL > 1
206             OSL_TRACE( "Unhandled case: pTypeRef->eTypeClass == %d\n", pTypeRef->eTypeClass );
207 #endif
208             OSL_ASSERT( 0 );
209     }
210     return false;
211 }
212 
return_in_hidden_param(typelib_TypeDescriptionReference * pTypeRef)213 bool aarch64::return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef )
214 {
215     int g, s;
216     // Returned in registers iff examine_argument() says it fits; otherwise the
217     // caller must pass an indirect-result buffer in x8.
218     return !examine_argument( pTypeRef, true, g, s );
219 }
220 
fill_struct(typelib_TypeDescriptionReference * pTypeRef,const sal_uInt64 * pGPR,const double * pFPR,void * pStruct)221 void aarch64::fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal_uInt64 *pGPR, const double *pFPR, void *pStruct )
222 {
223     int nUsedGPR = 0;
224     int nUsedFPR = 0;
225     if ( !examine_argument( pTypeRef, true, nUsedGPR, nUsedFPR ) )
226     {
227         // Should not happen: indirect returns are written through x8 directly,
228         // not scattered here.
229         OSL_ASSERT( 0 );
230         return;
231     }
232 
233     if ( nUsedFPR > 0 )
234     {
235         // HFA: each member occupies one v register; the members are contiguous
236         // in the struct.  Copy element-by-element to honour FLOAT (4-byte) vs
237         // DOUBLE (8-byte) element width.
238         HfaKind kind = HFA_NONE;
239         int count = 0;
240         collectHfa( pTypeRef, kind, count );
241         if ( kind == HFA_FLOAT )
242         {
243             float *pDest = reinterpret_cast<float *>( pStruct );
244             for ( int i = 0; i < nUsedFPR; ++i )
245                 pDest[i] = static_cast<float>( pFPR[i] );
246         }
247         else // HFA_DOUBLE
248         {
249             double *pDest = reinterpret_cast<double *>( pStruct );
250             for ( int i = 0; i < nUsedFPR; ++i )
251                 pDest[i] = pFPR[i];
252         }
253     }
254     else
255     {
256         // Non-HFA aggregate <= 16 bytes: raw copy of the 1-2 GPRs.
257         sal_uInt64 *pDest = reinterpret_cast<sal_uInt64 *>( pStruct );
258         for ( int i = 0; i < nUsedGPR; ++i )
259             pDest[i] = pGPR[i];
260     }
261 }
262