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