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