1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20
21 // MARKER(update_precomp.py): autogen include statement, do not remove
22 #include "precompiled_bridges.hxx"
23
24 // This is an implementation of the parameter-classification rules of the
25 // AArch64 procedure call standard ("Procedure Call Standard for the Arm 64-bit
26 // Architecture", ARM IHI 0055).
27 //
28 // Unlike the System V AMD64 ABI (used by the x86-64 bridge), AAPCS64 does not
29 // split aggregates into per-eightbyte INTEGER/SSE classes. Instead:
30 // * scalars go in one GPR (x) or one FP/SIMD (v) register;
31 // * a Homogeneous Floating-point Aggregate (HFA: <= 4 members, all the same
32 // FP type, recursively) goes in consecutive v registers;
33 // * any other aggregate <= 16 bytes goes in 1-2 GPRs;
34 // * a non-HFA aggregate > 16 bytes is passed indirectly (a pointer to a
35 // caller-allocated copy).
36 // Register fill is "all or nothing": if an aggregate does not fit entirely in
37 // the remaining registers of its bank, it is passed wholly on the stack.
38 //
39 // This is a clean-room implementation from the public specifications; see
40 // ../../../../AAPCS64_BRIDGE_SPEC.md.
41
42 #include "abi.hxx"
43
44 #include "bridges/cpp_uno/shared/types.hxx"
45
46 #include <rtl/ustring.hxx>
47 #include <string.h>
48
49 using namespace aarch64;
50
51 namespace {
52
53 // The element type of a Homogeneous Floating-point Aggregate.
54 enum HfaKind
55 {
56 HFA_NONE, // not (yet) an HFA
57 HFA_FLOAT, // all members are FLOAT (4-byte)
58 HFA_DOUBLE // all members are DOUBLE (8-byte)
59 };
60
61 // Combine the running HFA kind with a newly-seen member kind. Two members
62 // of different FP types, or any non-FP member, break the homogeneity.
mergeHfa(HfaKind running,HfaKind seen)63 HfaKind mergeHfa( HfaKind running, HfaKind seen )
64 {
65 if ( seen == HFA_NONE )
66 return HFA_NONE;
67 if ( running == HFA_NONE )
68 return seen;
69 return ( running == seen ) ? running : HFA_NONE;
70 }
71
isComplexAggregate(typelib_TypeDescriptionReference * pTypeRef)72 bool isComplexAggregate( typelib_TypeDescriptionReference *pTypeRef )
73 {
74 typelib_TypeDescription * pTypeDescr = 0;
75 TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
76 const typelib_CompoundTypeDescription *pComp =
77 reinterpret_cast<const typelib_CompoundTypeDescription *>( pTypeDescr );
78 bool complex = pComp->pBaseTypeDescription != 0 &&
79 isComplexAggregate( pComp->pBaseTypeDescription->aBase.pWeakRef );
80 for ( sal_Int32 i = 0; !complex && i < pComp->nMembers; ++i )
81 {
82 typelib_TypeClass typeClass = pComp->ppTypeRefs[i]->eTypeClass;
83 if ( typeClass == typelib_TypeClass_STRUCT ||
84 typeClass == typelib_TypeClass_EXCEPTION )
85 complex = isComplexAggregate( pComp->ppTypeRefs[i] );
86 else
87 complex = !bridges::cpp_uno::shared::isSimpleType( typeClass );
88 }
89 TYPELIB_DANGER_RELEASE( pTypeDescr );
90 return complex;
91 }
92
93 // Recursively determine whether pTypeRef is (part of) a homogeneous
94 // floating-point aggregate, accumulating the element kind and member count.
95 //
96 // Returns false the moment homogeneity is violated (a non-FP scalar, or a
97 // second distinct FP type, or > 4 elements). A FLOAT/DOUBLE scalar counts as
98 // a 1-element HFA of itself; a struct flattens its members (and base classes).
collectHfa(typelib_TypeDescriptionReference * pTypeRef,HfaKind & rKind,int & rCount)99 bool collectHfa( typelib_TypeDescriptionReference *pTypeRef, HfaKind &rKind, int &rCount )
100 {
101 switch ( pTypeRef->eTypeClass )
102 {
103 case typelib_TypeClass_FLOAT:
104 rKind = mergeHfa( rKind, HFA_FLOAT );
105 if ( rKind == HFA_NONE ) return false;
106 return ( ++rCount <= 4 );
107
108 case typelib_TypeClass_DOUBLE:
109 rKind = mergeHfa( rKind, HFA_DOUBLE );
110 if ( rKind == HFA_NONE ) return false;
111 return ( ++rCount <= 4 );
112
113 case typelib_TypeClass_STRUCT:
114 case typelib_TypeClass_EXCEPTION:
115 {
116 typelib_TypeDescription * pTypeDescr = 0;
117 TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
118
119 const typelib_CompoundTypeDescription *pComp =
120 reinterpret_cast<const typelib_CompoundTypeDescription*>( pTypeDescr );
121
122 // rCount is cumulative over the whole recursion, so remember where
123 // this aggregate started in order to size-check it below.
124 const int nCountAtEntry = rCount;
125 bool bOk = true;
126
127 // Flatten base class first (its members precede ours in layout).
128 if ( pComp->pBaseTypeDescription )
129 {
130 bOk = collectHfa(
131 pComp->pBaseTypeDescription->aBase.pWeakRef, rKind, rCount );
132 }
133
134 for ( sal_Int32 i = 0; bOk && i < pComp->nMembers; ++i )
135 bOk = collectHfa( pComp->ppTypeRefs[i], rKind, rCount );
136
137 if ( bOk )
138 {
139 // Reject anything the elements do not tile exactly: only the
140 // elements contributed by THIS aggregate count towards its size.
141 sal_Int32 elementSize = rKind == HFA_FLOAT ? 4 : 8;
142 bOk = pTypeDescr->nSize ==
143 ( rCount - nCountAtEntry ) * elementSize;
144 for ( sal_Int32 i = 0; bOk && i < pComp->nMembers; ++i )
145 bOk = pComp->pMemberOffsets[i] % elementSize == 0;
146 }
147
148 TYPELIB_DANGER_RELEASE( pTypeDescr );
149 return bOk;
150 }
151
152 default:
153 // Any non-FP, non-aggregate member breaks homogeneity.
154 rKind = HFA_NONE;
155 return false;
156 }
157 }
158
159 // Classify an aggregate (STRUCT/EXCEPTION). Sets the GPR/FPR counts and
160 // returns true if it is passed in registers, false if it must be passed
161 // indirectly (in memory).
classifyAggregate(typelib_TypeDescriptionReference * pTypeRef,int & nUsedGPR,int & nUsedFPR)162 bool classifyAggregate( typelib_TypeDescriptionReference *pTypeRef, int &nUsedGPR, int &nUsedFPR )
163 {
164 // First, the HFA test.
165 HfaKind kind = HFA_NONE;
166 int count = 0;
167 if ( collectHfa( pTypeRef, kind, count ) && kind != HFA_NONE )
168 {
169 nUsedFPR = count;
170 nUsedGPR = 0;
171 return true; // HFA passed in consecutive FP regs
172 }
173
174 // Not HFA: if bigger than 16 bytes, pass indirectly.
175 typelib_TypeDescription * pTypeDescr = 0;
176 TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
177 if ( pTypeDescr->nSize > 16 )
178 {
179 TYPELIB_DANGER_RELEASE( pTypeDescr );
180 nUsedGPR = nUsedFPR = 0;
181 return false; // indirect
182 }
183
184 // small aggregate: it occupies 1 or 2 GPRs depending on size
185 nUsedFPR = 0;
186 nUsedGPR = ( pTypeDescr->nSize + 7 ) / 8;
187 if ( nUsedGPR < 1 ) nUsedGPR = 1;
188 TYPELIB_DANGER_RELEASE( pTypeDescr );
189 return true;
190 }
191
192 } // anonymous namespace
193
194 // Public API implementations.
195 namespace aarch64
196 {
197
examine_argument(typelib_TypeDescriptionReference * pTypeRef,bool bInReturn,int & nUsedGPR,int & nUsedFPR)198 bool examine_argument( typelib_TypeDescriptionReference *pTypeRef, bool bInReturn, int &nUsedGPR, int &nUsedFPR )
199 {
200 // For returns, the hidden param rule uses >16 bytes for aggregates.
201 if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT || pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION )
202 {
203 return classifyAggregate( pTypeRef, nUsedGPR, nUsedFPR );
204 }
205
206 // Scalars: floats -> FPR, others -> GPR
207 switch ( pTypeRef->eTypeClass )
208 {
209 case typelib_TypeClass_FLOAT:
210 nUsedFPR = 1; nUsedGPR = 0; return true;
211 case typelib_TypeClass_DOUBLE:
212 nUsedFPR = 1; nUsedGPR = 0; return true;
213 default:
214 nUsedFPR = 0; nUsedGPR = 1; return true;
215 }
216 }
217
return_in_hidden_param(typelib_TypeDescriptionReference * pTypeRef)218 bool return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef )
219 {
220 if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT || pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION )
221 {
222 typelib_TypeDescription * pTypeDescr = 0;
223 TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
224 bool ret = pTypeDescr->nSize > 16;
225 TYPELIB_DANGER_RELEASE( pTypeDescr );
226 return ret;
227 }
228 return false; // scalars and small aggregates return in registers
229 }
230
get_return_kind(typelib_TypeDescriptionReference * pTypeRef)231 sal_uInt32 get_return_kind( typelib_TypeDescriptionReference *pTypeRef )
232 {
233 if ( pTypeRef->eTypeClass == typelib_TypeClass_FLOAT )
234 return typelib_TypeClass_FLOAT;
235
236 if ( pTypeRef->eTypeClass == typelib_TypeClass_DOUBLE )
237 return typelib_TypeClass_DOUBLE;
238
239 if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT ||
240 pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION )
241 {
242 HfaKind kind = HFA_NONE;
243 int count = 0;
244
245 if ( collectHfa( pTypeRef, kind, count ) )
246 {
247 if ( kind == HFA_FLOAT )
248 return RETURN_KIND_HFA_FLOAT;
249
250 if ( kind == HFA_DOUBLE )
251 return RETURN_KIND_HFA_DOUBLE;
252 }
253 }
254
255 return pTypeRef->eTypeClass;
256 }
257
fill_struct(typelib_TypeDescriptionReference * pTypeRef,const sal_uInt64 * pGPR,const double * pFPR,void * pStruct)258 void fill_struct( typelib_TypeDescriptionReference *pTypeRef, const sal_uInt64* pGPR, const double* pFPR, void *pStruct )
259 {
260 // For small aggregates, copy from GPR slots; for HFAs, copy from FPR slots.
261 if ( pTypeRef->eTypeClass == typelib_TypeClass_STRUCT || pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION )
262 {
263 int nGPR=0, nFPR=0;
264 if ( classifyAggregate( pTypeRef, nGPR, nFPR ) )
265 {
266 if ( nFPR > 0 )
267 {
268 // HFA: copy elements from FPR slots. For FLOAT HFAs each element is
269 // 4 bytes but occupies an 8-byte saved slot; copy each float from the
270 // low 4 bytes of the corresponding double-sized slot. DOUBLE HFAs
271 // can be copied directly.
272 HfaKind kind = HFA_NONE;
273 int count = 0;
274 if ( collectHfa( pTypeRef, kind, count ) && kind == HFA_FLOAT )
275 {
276 for ( int i = 0; i < nFPR; ++i )
277 memcpy(
278 static_cast<char *>( pStruct ) + i * sizeof(float),
279 reinterpret_cast<const char *>( pFPR) + i * sizeof(double),
280 sizeof(float) );
281 }
282 else
283 {
284 memcpy( pStruct, pFPR, nFPR * sizeof(double) );
285 }
286 }
287 else
288 {
289 memcpy( pStruct, pGPR, nGPR * sizeof(sal_uInt64) );
290 }
291 }
292 }
293 }
294
align_stack_offset(sal_uInt32 offset,typelib_TypeDescriptionReference * pTypeRef)295 sal_uInt32 align_stack_offset( sal_uInt32 offset, typelib_TypeDescriptionReference *pTypeRef )
296 {
297 // AArch64 stack overflow area is packed; align to natural alignment of the type (8)
298 const sal_uInt32 align = 8;
299 return ( offset + align - 1 ) & ~( align - 1 );
300 }
301
stack_size(typelib_TypeDescriptionReference * pTypeRef)302 sal_uInt32 stack_size( typelib_TypeDescriptionReference *pTypeRef )
303 {
304 // For simple types and small aggregates, size is rounded to 8
305 typelib_TypeDescription * pTypeDescr = 0;
306 TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
307 sal_uInt32 size = pTypeDescr->nSize;
308 TYPELIB_DANGER_RELEASE( pTypeDescr );
309 return ( size + 7 ) & ~7u;
310 }
311
312 } // namespace aarch64
313