xref: /trunk/main/bridges/source/cpp_uno/gcc3_freebsd_aarch64/abi.cxx (revision 56e4d38c33928bade1ec98e3d1fd9ebea94cde1d)
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.
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 
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).
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).
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 
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 
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 
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 
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 
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 
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