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