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.
mergeHfa(HfaKind running,HfaKind seen)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
isComplexAggregate(typelib_TypeDescriptionReference * pTypeRef)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).
collectHfa(typelib_TypeDescriptionReference * pTypeRef,HfaKind & rKind,int & rCount)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).
classifyAggregate(typelib_TypeDescriptionReference * pTypeRef,int & nUsedGPR,int & nUsedFPR)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
examine_argument(typelib_TypeDescriptionReference * pTypeRef,bool bInReturn,int & nUsedGPR,int & nUsedFPR)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
return_in_hidden_param(typelib_TypeDescriptionReference * pTypeRef)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
get_return_kind(typelib_TypeDescriptionReference * pTypeRef)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
fill_struct(typelib_TypeDescriptionReference * pTypeRef,const sal_uInt64 * pGPR,const double * pFPR,void * pStruct)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
align_stack_offset(sal_uInt32 offset,typelib_TypeDescriptionReference * pTypeRef)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
stack_size(typelib_TypeDescriptionReference * pTypeRef)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