1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "aarch64_cgfunc.h"
17 #include "becommon.h"
18 #include "aarch64_call_conv.h"
19
20 namespace maplebe {
21 using namespace maple;
22
23 namespace {
24 constexpr int kMaxRegCount = 4;
25
26 /*
27 * Refer to ARM IHI 0055C_beta: Procedure Call Standard for
28 * ARM 64-bit Architecture. Table 1.
29 */
30 enum AArch64ArgumentClass : uint8 { kAArch64NoClass, kAArch64IntegerClass, kAArch64FloatClass, kAArch64MemoryClass };
31
ProcessNonStructAndNonArrayWhenClassifyAggregate(const MIRType & mirType,AArch64ArgumentClass classes[kMaxRegCount],size_t classesLength)32 int32 ProcessNonStructAndNonArrayWhenClassifyAggregate(const MIRType &mirType,
33 AArch64ArgumentClass classes[kMaxRegCount], size_t classesLength)
34 {
35 CHECK_FATAL(classesLength > 0, "classLength must > 0");
36 /* scalar type */
37 switch (mirType.GetPrimType()) {
38 case PTY_u1:
39 case PTY_u8:
40 case PTY_i8:
41 case PTY_u16:
42 case PTY_i16:
43 case PTY_a32:
44 case PTY_u32:
45 case PTY_i32:
46 case PTY_a64:
47 case PTY_ptr:
48 case PTY_ref:
49 case PTY_u64:
50 case PTY_i64:
51 classes[0] = kAArch64IntegerClass;
52 return 1;
53 case PTY_f32:
54 case PTY_f64:
55 case PTY_c64:
56 case PTY_c128:
57 classes[0] = kAArch64FloatClass;
58 return 1;
59 default:
60 CHECK_FATAL(false, "NYI");
61 }
62
63 /* should not reach to this point */
64 return 0;
65 }
66
TraverseStructFieldsForFp(MIRType * ty,uint32 & numRegs)67 PrimType TraverseStructFieldsForFp(MIRType *ty, uint32 &numRegs)
68 {
69 if (ty->GetKind() == kTypeArray) {
70 MIRArrayType *arrtype = static_cast<MIRArrayType *>(ty);
71 MIRType *pty = GlobalTables::GetTypeTable().GetTypeFromTyIdx(arrtype->GetElemTyIdx());
72 if (pty->GetKind() == kTypeArray || pty->GetKind() == kTypeStruct) {
73 return TraverseStructFieldsForFp(pty, numRegs);
74 }
75 for (uint32 i = 0; i < arrtype->GetDim(); ++i) {
76 numRegs += arrtype->GetSizeArrayItem(i);
77 }
78 return pty->GetPrimType();
79 } else if (ty->GetKind() == kTypeStruct) {
80 MIRStructType *sttype = static_cast<MIRStructType *>(ty);
81 FieldVector fields = sttype->GetFields();
82 PrimType oldtype = PTY_void;
83 for (uint32 fcnt = 0; fcnt < fields.size(); ++fcnt) {
84 TyIdx fieldtyidx = fields[fcnt].second.first;
85 MIRType *fieldty = GlobalTables::GetTypeTable().GetTypeFromTyIdx(fieldtyidx);
86 PrimType ptype = TraverseStructFieldsForFp(fieldty, numRegs);
87 if (oldtype != PTY_void && oldtype != ptype) {
88 return PTY_void;
89 } else {
90 oldtype = ptype;
91 }
92 }
93 return oldtype;
94 } else {
95 numRegs++;
96 return ty->GetPrimType();
97 }
98 }
99
100 int32 ClassifyAggregate(const BECommon &be, MIRType &mirType, AArch64ArgumentClass classes[kMaxRegCount],
101 size_t classesLength, uint32 &fpSize);
102
ProcessStructWhenClassifyAggregate(const BECommon & be,MIRStructType & structType,AArch64ArgumentClass classes[kMaxRegCount],size_t classesLength,uint32 & fpSize)103 uint32 ProcessStructWhenClassifyAggregate(const BECommon &be, MIRStructType &structType,
104 AArch64ArgumentClass classes[kMaxRegCount], size_t classesLength,
105 uint32 &fpSize)
106 {
107 CHECK_FATAL(classesLength > 0, "classLength must > 0");
108 uint32 sizeOfTyInDwords =
109 static_cast<uint32>(RoundUp(be.GetTypeSize(structType.GetTypeIndex()), k8ByteSize) >> k8BitShift);
110 bool isF32 = false;
111 bool isF64 = false;
112 uint32 numRegs = 0;
113 for (uint32 f = 0; f < structType.GetFieldsSize(); ++f) {
114 TyIdx fieldTyIdx = structType.GetFieldsElemt(f).second.first;
115 MIRType *fieldType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(fieldTyIdx);
116 PrimType pType = TraverseStructFieldsForFp(fieldType, numRegs);
117 if (pType == PTY_f32) {
118 if (isF64) {
119 isF64 = false;
120 break;
121 }
122 isF32 = true;
123 } else if (pType == PTY_f64) {
124 if (isF32) {
125 isF32 = false;
126 break;
127 }
128 isF64 = true;
129 } else if (IsPrimitiveVector(pType)) {
130 isF64 = true;
131 break;
132 } else {
133 isF32 = isF64 = false;
134 break;
135 }
136 }
137 if (isF32 || isF64) {
138 CHECK_FATAL(numRegs <= classesLength, "ClassifyAggregate: num regs exceed limit");
139 for (uint32 i = 0; i < numRegs; ++i) {
140 classes[i] = kAArch64FloatClass;
141 }
142 fpSize = isF32 ? k4ByteSize : k8ByteSize;
143 return numRegs;
144 }
145
146 classes[0] = kAArch64IntegerClass;
147 if (sizeOfTyInDwords == kDwordSizeTwo) {
148 classes[1] = kAArch64IntegerClass;
149 }
150 DEBUG_ASSERT(sizeOfTyInDwords <= classesLength, "sizeOfTyInDwords exceed limit");
151 return sizeOfTyInDwords;
152 }
153
154 /*
155 * Analyze the given aggregate using the rules given by the ARM 64-bit ABI and
156 * return the number of doublewords to be passed in registers; the classes of
157 * the doublewords are returned in parameter "classes"; if 0 is returned, it
158 * means the whole aggregate is passed in memory.
159 */
ClassifyAggregate(const BECommon & be,MIRType & mirType,AArch64ArgumentClass classes[kMaxRegCount],size_t classesLength,uint32 & fpSize)160 int32 ClassifyAggregate(const BECommon &be, MIRType &mirType, AArch64ArgumentClass classes[kMaxRegCount],
161 size_t classesLength, uint32 &fpSize)
162 {
163 CHECK_FATAL(classesLength > 0, "invalid index");
164 uint64 sizeOfTy = be.GetTypeSize(mirType.GetTypeIndex());
165 /* Rule B.3.
166 * If the argument type is a Composite Type that is larger than 16 bytes
167 * then the argument is copied to memory allocated by the caller and
168 * the argument is replaced by a pointer to the copy.
169 */
170 if ((sizeOfTy > k16ByteSize) || (sizeOfTy == 0)) {
171 return 0;
172 }
173
174 /*
175 * An argument of any Integer class takes up an integer register
176 * which is a single double-word.
177 * Rule B.4. The size of an argument of composite type is rounded up to the nearest
178 * multiple of 8 bytes.
179 */
180 int64 sizeOfTyInDwords = static_cast<int64>(RoundUp(sizeOfTy, k8ByteSize) >> k8BitShift);
181 DEBUG_ASSERT(sizeOfTyInDwords > 0, "sizeOfTyInDwords should be sizeOfTyInDwords > 0");
182 DEBUG_ASSERT(sizeOfTyInDwords <= kTwoRegister, "sizeOfTyInDwords should be <= 2");
183 int64 i;
184 for (i = 0; i < sizeOfTyInDwords; ++i) {
185 classes[i] = kAArch64NoClass;
186 }
187 if ((mirType.GetKind() != kTypeStruct) && (mirType.GetKind() != kTypeArray) && (mirType.GetKind() != kTypeUnion)) {
188 return ProcessNonStructAndNonArrayWhenClassifyAggregate(mirType, classes, classesLength);
189 }
190 if (mirType.GetKind() == kTypeStruct) {
191 MIRStructType &structType = static_cast<MIRStructType &>(mirType);
192 return static_cast<int32>(ProcessStructWhenClassifyAggregate(be, structType, classes, classesLength, fpSize));
193 }
194 /* post merger clean-up */
195 for (i = 0; i < sizeOfTyInDwords; ++i) {
196 if (classes[i] == kAArch64MemoryClass) {
197 return 0;
198 }
199 }
200 return static_cast<int32>(sizeOfTyInDwords);
201 }
202 } // namespace
203
204 /* external interface to look for pure float struct */
FloatParamRegRequired(MIRStructType & structType,uint32 & fpSize)205 uint32 AArch64CallConvImpl::FloatParamRegRequired(MIRStructType &structType, uint32 &fpSize)
206 {
207 if (structType.GetSize() > k32ByteSize) {
208 return 0;
209 }
210 AArch64ArgumentClass classes[kMaxRegCount];
211 uint32 numRegs = ProcessStructWhenClassifyAggregate(beCommon, structType, classes, kMaxRegCount, fpSize);
212 if (numRegs == 0) {
213 return 0;
214 }
215
216 bool isPure = true;
217 for (uint32 i = 0; i < numRegs; ++i) {
218 DEBUG_ASSERT(i < kMaxRegCount, "i should be lower than kMaxRegCount");
219 if (classes[i] != kAArch64FloatClass) {
220 isPure = false;
221 break;
222 }
223 }
224 if (isPure) {
225 return numRegs;
226 }
227 return 0;
228 }
229
LocateRetVal(MIRType & retType,CCLocInfo & pLoc)230 int32 AArch64CallConvImpl::LocateRetVal(MIRType &retType, CCLocInfo &pLoc)
231 {
232 InitCCLocInfo(pLoc);
233 uint32 retSize = beCommon.GetTypeSize(retType.GetTypeIndex().GetIdx());
234 if (retSize == 0) {
235 return 0; /* size 0 ret val */
236 }
237 if (retSize <= k16ByteSize) {
238 /* For return struct size less or equal to 16 bytes, the values */
239 /* are returned in register pairs. */
240 AArch64ArgumentClass classes[kMaxRegCount] = {kAArch64NoClass}; /* Max of four floats. */
241 uint32 fpSize;
242 uint32 numRegs = static_cast<uint32>(ClassifyAggregate(beCommon, retType, classes, sizeof(classes), fpSize));
243 if (classes[0] == kAArch64FloatClass) {
244 CHECK_FATAL(numRegs <= kMaxRegCount, "LocateNextParm: illegal number of regs");
245 AllocateNSIMDFPRegisters(pLoc, numRegs);
246 pLoc.numFpPureRegs = numRegs;
247 pLoc.fpSize = fpSize;
248 return 0;
249 } else {
250 CHECK_FATAL(numRegs <= kTwoRegister, "LocateNextParm: illegal number of regs");
251 if (numRegs == kOneRegister) {
252 pLoc.reg0 = AllocateGPRegister();
253 } else {
254 AllocateTwoGPRegisters(pLoc);
255 }
256 return 0;
257 }
258 } else {
259 /* For return struct size > 16 bytes the pointer returns in x8. */
260 pLoc.reg0 = R8;
261 return GetPointerSize();
262 }
263 }
264
265 /*
266 * Refer to ARM IHI 0055C_beta: Procedure Call Standard for
267 * the ARM 64-bit Architecture. $5.4.2
268 *
269 * For internal only functions, we may want to implement
270 * our own rules as Apple IOS has done. Maybe we want to
271 * generate two versions for each of externally visible functions,
272 * one conforming to the ARM standard ABI, and the other for
273 * internal only use.
274 *
275 * LocateNextParm should be called with each parameter in the parameter list
276 * starting from the beginning, one call per parameter in sequence; it returns
277 * the information on how each parameter is passed in pLoc
278 *
279 * *** CAUTION OF USE: ***
280 * If LocateNextParm is called for function formals, third argument isFirst is true.
281 * LocateNextParm is then checked against a function parameter list. All other calls
282 * of LocateNextParm are against caller's argument list must not have isFirst set,
283 * or it will be checking the caller's enclosing function.
284 */
285
LocateNextParm(MIRType & mirType,CCLocInfo & pLoc,bool isFirst,MIRFunction * tFunc)286 int32 AArch64CallConvImpl::LocateNextParm(MIRType &mirType, CCLocInfo &pLoc, bool isFirst, MIRFunction *tFunc)
287 {
288 InitCCLocInfo(pLoc);
289 // C calling convention.
290 bool is64x1vec = false;
291
292 if (isFirst) {
293 MIRFunction *func = tFunc != nullptr ? tFunc : const_cast<MIRFunction *>(beCommon.GetMIRModule().CurFunction());
294 if (func->IsFirstArgReturn()) {
295 TyIdx tyIdx = func->GetFuncRetStructTyIdx();
296 size_t size = beCommon.GetTypeSize(tyIdx);
297 if (size == 0) {
298 /* For return struct size 0 there is no return value. */
299 return 0;
300 }
301 /* For return struct size > 16 bytes the pointer returns in x8. */
302 pLoc.reg0 = R8;
303 return GetPointerSize();
304 }
305 }
306 uint64 typeSize = beCommon.GetTypeSize(mirType.GetTypeIndex());
307 if (typeSize == 0) {
308 return 0;
309 }
310 int32 typeAlign = beCommon.GetTypeAlign(mirType.GetTypeIndex());
311 /*
312 * Rule C.12 states that we do round nextStackArgAdress up before we use its value
313 * according to the alignment requirement of the argument being processed.
314 * We do the rounding up at the end of LocateNextParm(),
315 * so we want to make sure our rounding up is correct.
316 */
317 DEBUG_ASSERT((nextStackArgAdress & (std::max(typeAlign, static_cast<int32>(k8ByteSize)) - 1)) == 0,
318 "C.12 alignment requirement is violated");
319 pLoc.memSize = static_cast<int32>(typeSize);
320 ++paramNum;
321
322 int32 aggCopySize = 0;
323 switch (mirType.GetPrimType()) {
324 case PTY_u1:
325 case PTY_u8:
326 case PTY_i8:
327 case PTY_u16:
328 case PTY_i16:
329 case PTY_a32:
330 case PTY_u32:
331 case PTY_i32:
332 case PTY_ptr:
333 case PTY_ref:
334 case PTY_a64:
335 case PTY_u64:
336 case PTY_i64:
337 case PTY_i128:
338 case PTY_u128:
339 /* Rule C.7 */
340 typeSize = k8ByteSize;
341 pLoc.reg0 = is64x1vec ? AllocateSIMDFPRegister() : AllocateGPRegister();
342 DEBUG_ASSERT(nextGeneralRegNO <= AArch64Abi::kNumIntParmRegs, "RegNo should be pramRegNO");
343 break;
344 /*
345 * for c64 complex numbers, we assume
346 * - callers marshall the two f32 numbers into one f64 register
347 * - callees de-marshall one f64 value into the real and the imaginery part
348 */
349 case PTY_f32:
350 case PTY_f64:
351 case PTY_c64:
352 case PTY_v2i32:
353 case PTY_v4i16:
354 case PTY_v8i8:
355 case PTY_v2u32:
356 case PTY_v4u16:
357 case PTY_v8u8:
358 case PTY_v2f32:
359 /* Rule C.1 */
360 DEBUG_ASSERT(GetPrimTypeSize(PTY_f64) == k8ByteSize, "unexpected type size");
361 typeSize = k8ByteSize;
362 pLoc.reg0 = AllocateSIMDFPRegister();
363 break;
364 /*
365 * for c128 complex numbers, we assume
366 * - callers marshall the two f64 numbers into one f128 register
367 * - callees de-marshall one f128 value into the real and the imaginery part
368 */
369 case PTY_c128:
370 case PTY_v2i64:
371 case PTY_v4i32:
372 case PTY_v8i16:
373 case PTY_v16i8:
374 case PTY_v2u64:
375 case PTY_v4u32:
376 case PTY_v8u16:
377 case PTY_v16u8:
378 case PTY_v2f64:
379 case PTY_v4f32:
380 /* SIMD-FP registers have 128-bits. */
381 pLoc.reg0 = AllocateSIMDFPRegister();
382 DEBUG_ASSERT(nextFloatRegNO <= AArch64Abi::kNumFloatParmRegs,
383 "regNO should not be greater than kNumFloatParmRegs");
384 DEBUG_ASSERT(typeSize == k16ByteSize, "unexpected type size");
385 break;
386 /*
387 * case of quad-word integer:
388 * we don't support java yet.
389 * if (has-16-byte-alignment-requirement)
390 * nextGeneralRegNO = (nextGeneralRegNO+1) & ~1; // C.8 round it up to the next even number
391 * try allocate two consecutive registers at once.
392 */
393 /* case PTY_agg */
394 case PTY_agg: {
395 aggCopySize = ProcessPtyAggWhenLocateNextParm(mirType, pLoc, typeSize, typeAlign);
396 break;
397 }
398 default:
399 CHECK_FATAL(false, "NYI");
400 }
401
402 /* Rule C.12 */
403 if (pLoc.reg0 == kRinvalid) {
404 /* being passed in memory */
405 nextStackArgAdress = pLoc.memOffset + static_cast<int32>(static_cast<int64>(typeSize));
406 }
407 return aggCopySize;
408 }
409
ProcessPtyAggWhenLocateNextParm(MIRType & mirType,CCLocInfo & pLoc,uint64 & typeSize,int32 typeAlign)410 int32 AArch64CallConvImpl::ProcessPtyAggWhenLocateNextParm(MIRType &mirType, CCLocInfo &pLoc, uint64 &typeSize,
411 int32 typeAlign)
412 {
413 /*
414 * In AArch64, integer-float or float-integer
415 * argument passing is not allowed. All should go through
416 * integer-integer.
417 * In the case where a struct is homogeneous composed of one of the fp types,
418 * either all single fp or all double fp, then it can be passed by float-float.
419 */
420 AArch64ArgumentClass classes[kMaxRegCount] = {kAArch64NoClass};
421 typeSize = beCommon.GetTypeSize(mirType.GetTypeIndex().GetIdx());
422 int32 aggCopySize = 0;
423 if (typeSize > k16ByteSize) {
424 aggCopySize = static_cast<int32>(RoundUp(typeSize, GetPointerSize()));
425 }
426 /*
427 * alignment requirement
428 * Note. This is one of a few things iOS diverges from
429 * the ARM 64-bit standard. They don't observe the round-up requirement.
430 */
431 if (typeAlign == k16ByteSize) {
432 RoundNGRNUpToNextEven();
433 }
434
435 uint32 fpSize;
436 uint32 numRegs = static_cast<uint32>(
437 ClassifyAggregate(beCommon, mirType, classes, sizeof(classes) / sizeof(AArch64ArgumentClass), fpSize));
438 if (classes[0] == kAArch64FloatClass) {
439 CHECK_FATAL(numRegs <= kMaxRegCount, "LocateNextParm: illegal number of regs");
440 typeSize = k8ByteSize;
441 AllocateNSIMDFPRegisters(pLoc, numRegs);
442 pLoc.numFpPureRegs = numRegs;
443 pLoc.fpSize = fpSize;
444 } else if (numRegs == 1) {
445 /* passing in registers */
446 typeSize = k8ByteSize;
447 if (classes[0] == kAArch64FloatClass) {
448 CHECK_FATAL(false, "param passing in FP reg not allowed here");
449 } else {
450 pLoc.reg0 = AllocateGPRegister();
451 /* Rule C.11 */
452 DEBUG_ASSERT((pLoc.reg0 != kRinvalid) || (nextGeneralRegNO == AArch64Abi::kNumIntParmRegs),
453 "reg0 should not be kRinvalid or nextGeneralRegNO should equal kNumIntParmRegs");
454 }
455 } else if (numRegs == kTwoRegister) {
456 /* Other aggregates with 8 < size <= 16 bytes can be allocated in reg pair */
457 DEBUG_ASSERT(classes[0] == kAArch64IntegerClass || classes[0] == kAArch64NoClass,
458 "classes[0] must be either integer class or no class");
459 DEBUG_ASSERT(classes[1] == kAArch64IntegerClass || classes[1] == kAArch64NoClass,
460 "classes[1] must be either integer class or no class");
461 AllocateTwoGPRegisters(pLoc);
462 /* Rule C.11 */
463 if (pLoc.reg0 == kRinvalid) {
464 nextGeneralRegNO = AArch64Abi::kNumIntParmRegs;
465 }
466 } else {
467 /*
468 * 0 returned from ClassifyAggregate(). This means the whole data
469 * is passed thru memory.
470 * Rule B.3.
471 * If the argument type is a Composite Type that is larger than 16
472 * bytes then the argument is copied to memory allocated by the
473 * caller and the argument is replaced by a pointer to the copy.
474 *
475 * Try to allocate an integer register
476 */
477 typeSize = k8ByteSize;
478 pLoc.reg0 = AllocateGPRegister();
479 pLoc.memSize = k8ByteSizeInt; /* byte size of a pointer in AArch64 */
480 if (pLoc.reg0 != kRinvalid) {
481 numRegs = 1;
482 }
483 }
484 /* compute rightpad */
485 if ((numRegs == 0) || (pLoc.reg0 == kRinvalid)) {
486 /* passed in memory */
487 typeSize = RoundUp(static_cast<uint64>(static_cast<int64>(pLoc.memSize)), k8ByteSize);
488 }
489 return aggCopySize;
490 }
491
492 /*
493 * instantiated with the type of the function return value, it describes how
494 * the return value is to be passed back to the caller
495 *
496 * Refer to ARM IHI 0055C_beta: Procedure Call Standard for
497 * the ARM 64-bit Architecture. $5.5
498 * "If the type, T, of the result of a function is such that
499 * void func(T arg)
500 * would require that 'arg' be passed as a value in a register
501 * (or set of registers) according to the rules in $5.4 Parameter
502 * Passing, then the result is returned in the same registers
503 * as would be used for such an argument.
504 */
InitReturnInfo(MIRType & retTy,CCLocInfo & ccLocInfo)505 void AArch64CallConvImpl::InitReturnInfo(MIRType &retTy, CCLocInfo &ccLocInfo)
506 {
507 PrimType pType = retTy.GetPrimType();
508 switch (pType) {
509 case PTY_void:
510 break;
511 case PTY_u1:
512 case PTY_u8:
513 case PTY_i8:
514 case PTY_u16:
515 case PTY_i16:
516 case PTY_a32:
517 case PTY_u32:
518 case PTY_i32:
519 ccLocInfo.regCount = 1;
520 ccLocInfo.reg0 = AArch64Abi::intReturnRegs[0];
521 ccLocInfo.primTypeOfReg0 = IsSignedInteger(pType) ? PTY_i32 : PTY_u32; /* promote the type */
522 return;
523
524 case PTY_ptr:
525 case PTY_ref:
526 CHECK_FATAL(false, "PTY_ptr should have been lowered");
527 return;
528
529 case PTY_a64:
530 case PTY_u64:
531 case PTY_i64:
532 case PTY_i128:
533 case PTY_u128:
534 ccLocInfo.regCount = 1;
535 ccLocInfo.reg0 = AArch64Abi::intReturnRegs[0];
536 ccLocInfo.primTypeOfReg0 = IsSignedInteger(pType) ? PTY_i64 : PTY_u64; /* promote the type */
537 return;
538
539 /*
540 * for c64 complex numbers, we assume
541 * - callers marshall the two f32 numbers into one f64 register
542 * - callees de-marshall one f64 value into the real and the imaginery part
543 */
544 case PTY_f32:
545 case PTY_f64:
546 case PTY_c64:
547 case PTY_v2i32:
548 case PTY_v4i16:
549 case PTY_v8i8:
550 case PTY_v2u32:
551 case PTY_v4u16:
552 case PTY_v8u8:
553 case PTY_v2f32:
554
555 /*
556 * for c128 complex numbers, we assume
557 * - callers marshall the two f64 numbers into one f128 register
558 * - callees de-marshall one f128 value into the real and the imaginery part
559 */
560 case PTY_c128:
561 case PTY_v2i64:
562 case PTY_v4i32:
563 case PTY_v8i16:
564 case PTY_v16i8:
565 case PTY_v2u64:
566 case PTY_v4u32:
567 case PTY_v8u16:
568 case PTY_v16u8:
569 case PTY_v2f64:
570 case PTY_v4f32:
571 ccLocInfo.regCount = 1;
572 ccLocInfo.reg0 = AArch64Abi::floatReturnRegs[0];
573 ccLocInfo.primTypeOfReg0 = pType;
574 return;
575
576 /*
577 * Refer to ARM IHI 0055C_beta: Procedure Call Standard for
578 * the ARM 64-bit Architecture. $5.5
579 * "Otherwise, the caller shall reserve a block of memory of
580 * sufficient size and alignment to hold the result. The
581 * address of the memory block shall be passed as an additional
582 * argument to the function in x8. The callee may modify the
583 * result memory block at any point during the execution of the
584 * subroutine (there is no requirement for the callee to preserve
585 * the value stored in x8)."
586 */
587 case PTY_agg: {
588 uint64 size = beCommon.GetTypeSize(retTy.GetTypeIndex());
589 if ((size > k16ByteSize) || (size == 0)) {
590 /*
591 * The return value is returned via memory.
592 * The address is in X8 and passed by the caller.
593 */
594 SetupToReturnThroughMemory(ccLocInfo);
595 return;
596 }
597 uint32 fpSize;
598 AArch64ArgumentClass classes[kMaxRegCount] = {kAArch64NoClass};
599 ccLocInfo.regCount = static_cast<uint8>(
600 ClassifyAggregate(beCommon, retTy, classes, sizeof(classes) / sizeof(AArch64ArgumentClass), fpSize));
601 if (classes[0] == kAArch64FloatClass) {
602 switch (ccLocInfo.regCount) {
603 case kFourRegister:
604 ccLocInfo.reg3 = AArch64Abi::floatReturnRegs[kFourthReg];
605 break;
606 case kThreeRegister:
607 ccLocInfo.reg2 = AArch64Abi::floatReturnRegs[kThirdReg];
608 break;
609 case kTwoRegister:
610 ccLocInfo.reg1 = AArch64Abi::floatReturnRegs[kSecondReg];
611 break;
612 case kOneRegister:
613 ccLocInfo.reg0 = AArch64Abi::floatReturnRegs[kFirstReg];
614 break;
615 default:
616 CHECK_FATAL(0, "AArch64CallConvImpl: unsupported");
617 }
618 if (fpSize == k4ByteSize) {
619 ccLocInfo.primTypeOfReg0 = ccLocInfo.primTypeOfReg1 = PTY_f32;
620 } else {
621 ccLocInfo.primTypeOfReg0 = ccLocInfo.primTypeOfReg1 = PTY_f64;
622 }
623 return;
624 } else if (ccLocInfo.regCount == 0) {
625 SetupToReturnThroughMemory(ccLocInfo);
626 return;
627 } else {
628 if (ccLocInfo.regCount == 1) {
629 /* passing in registers */
630 if (classes[0] == kAArch64FloatClass) {
631 ccLocInfo.reg0 = AArch64Abi::floatReturnRegs[0];
632 ccLocInfo.primTypeOfReg0 = PTY_f64;
633 } else {
634 ccLocInfo.reg0 = AArch64Abi::intReturnRegs[0];
635 ccLocInfo.primTypeOfReg0 = PTY_i64;
636 }
637 } else {
638 DEBUG_ASSERT(ccLocInfo.regCount <= k2ByteSize,
639 "reg count from ClassifyAggregate() should be 0, 1, or 2");
640 DEBUG_ASSERT(classes[0] == kAArch64IntegerClass, "error val :classes[0]");
641 DEBUG_ASSERT(classes[1] == kAArch64IntegerClass, "error val :classes[1]");
642 ccLocInfo.reg0 = AArch64Abi::intReturnRegs[0];
643 ccLocInfo.primTypeOfReg0 = PTY_i64;
644 ccLocInfo.reg1 = AArch64Abi::intReturnRegs[1];
645 ccLocInfo.primTypeOfReg1 = PTY_i64;
646 }
647 return;
648 }
649 }
650 default:
651 CHECK_FATAL(false, "NYI");
652 }
653 }
654
SetupSecondRetReg(const MIRType & retTy2,CCLocInfo & pLoc) const655 void AArch64CallConvImpl::SetupSecondRetReg(const MIRType &retTy2, CCLocInfo &pLoc) const
656 {
657 DEBUG_ASSERT(pLoc.reg1 == kRinvalid, "make sure reg1 equal kRinvalid");
658 PrimType pType = retTy2.GetPrimType();
659 switch (pType) {
660 case PTY_void:
661 break;
662 case PTY_u1:
663 case PTY_u8:
664 case PTY_i8:
665 case PTY_u16:
666 case PTY_i16:
667 case PTY_a32:
668 case PTY_u32:
669 case PTY_i32:
670 case PTY_ptr:
671 case PTY_ref:
672 case PTY_a64:
673 case PTY_u64:
674 case PTY_i64:
675 pLoc.reg1 = AArch64Abi::intReturnRegs[1];
676 pLoc.primTypeOfReg1 = IsSignedInteger(pType) ? PTY_i64 : PTY_u64; /* promote the type */
677 break;
678 default:
679 CHECK_FATAL(false, "NYI");
680 }
681 }
682
LocateNextParm(MIRType & mirType,CCLocInfo & pLoc,bool isFirst,MIRFunction * tFunc)683 int32 AArch64WebKitJSCC::LocateNextParm(MIRType &mirType, CCLocInfo &pLoc, bool isFirst, MIRFunction *tFunc)
684 {
685 std::vector<ArgumentClass> classes {};
686 int32 alignedTySize = ClassificationArg(beCommon, mirType, classes);
687 pLoc.memSize = alignedTySize;
688 if (classes[0] == kIntegerClass) {
689 if (alignedTySize == k8ByteSize) {
690 pLoc.reg0 = AllocateGPParmRegister();
691 } else {
692 CHECK_FATAL(false, "no should not go here");
693 }
694 } else if (classes[0] == kFloatClass) {
695 CHECK_FATAL(false, "float should passed on stack!");
696 }
697 if (pLoc.reg0 == kRinvalid || classes[0] == kMemoryClass) {
698 /* being passed in memory */
699 pLoc.memOffset = nextStackArgAdress;
700 nextStackArgAdress = pLoc.memOffset + alignedTySize;
701 }
702 return 0;
703 }
704
LocateRetVal(MIRType & retType,CCLocInfo & pLoc)705 int32 AArch64WebKitJSCC::LocateRetVal(MIRType &retType, CCLocInfo &pLoc)
706 {
707 InitCCLocInfo(pLoc);
708 std::vector<ArgumentClass> classes {}; /* Max of four Regs. */
709 int32 alignedTySize = ClassificationRet(beCommon, retType, classes);
710 if (alignedTySize == 0) {
711 return 0; /* size 0 ret val */
712 }
713 if (classes[0] == kIntegerClass) {
714 if ((alignedTySize == k4ByteSize) || (alignedTySize == k8ByteSize)) {
715 pLoc.reg0 = AllocateGPRetRegister();
716 pLoc.regCount += 1;
717 pLoc.primTypeOfReg0 = alignedTySize == k4ByteSize ? PTY_i32 : PTY_i64;
718 } else {
719 CHECK_FATAL(false, "should not go here");
720 }
721 } else if (classes[0] == kFloatClass) {
722 if ((alignedTySize == k4ByteSize) || (alignedTySize == k8ByteSize)) {
723 pLoc.reg0 = AllocateSIMDFPRetRegister();
724 pLoc.regCount += 1;
725 pLoc.primTypeOfReg0 = alignedTySize == k4ByteSize ? PTY_f32 : PTY_f64;
726 } else {
727 CHECK_FATAL(false, "should not go here");
728 }
729 }
730 if (pLoc.reg0 == kRinvalid || classes[0] == kMemoryClass) {
731 CHECK_FATAL(false, "should not happen");
732 }
733 return 0;
734 }
735
ClassificationRet(const BECommon & be,MIRType & mirType,std::vector<ArgumentClass> & classes) const736 int32 AArch64WebKitJSCC::ClassificationRet(const BECommon &be, MIRType &mirType,
737 std::vector<ArgumentClass> &classes) const
738 {
739 switch (mirType.GetPrimType()) {
740 /*
741 * Arguments of types void, (signed and unsigned) _Bool, char, short, int,
742 * long, long long, and pointers are in the INTEGER class.
743 */
744 case PTY_u32:
745 case PTY_i32:
746 classes.push_back(kIntegerClass);
747 return k4ByteSize;
748 case PTY_a64:
749 case PTY_ptr:
750 case PTY_ref:
751 case PTY_u64:
752 case PTY_i64:
753 classes.push_back(kIntegerClass);
754 return k8ByteSize;
755 case PTY_f32:
756 classes.push_back(kFloatClass);
757 return k4ByteSize;
758 case PTY_f64:
759 classes.push_back(kFloatClass);
760 return k8ByteSize;
761 default:
762 CHECK_FATAL(false, "NYI");
763 }
764 }
765
ClassificationArg(const BECommon & be,MIRType & mirType,std::vector<ArgumentClass> & classes) const766 int32 AArch64WebKitJSCC::ClassificationArg(const BECommon &be, MIRType &mirType,
767 std::vector<ArgumentClass> &classes) const
768 {
769 switch (mirType.GetPrimType()) {
770 /*
771 * Arguments of types void, (signed and unsigned) _Bool, char, short, int,
772 * long, long long, and pointers are in the INTEGER class.
773 */
774 case PTY_void:
775 case PTY_u1:
776 case PTY_u8:
777 case PTY_i8:
778 case PTY_u16:
779 case PTY_i16:
780 case PTY_a32:
781 case PTY_u32:
782 case PTY_i32:
783 classes.push_back(kIntegerClass);
784 return k4ByteSize;
785 case PTY_a64:
786 case PTY_ptr:
787 case PTY_ref:
788 case PTY_u64:
789 case PTY_i64:
790 classes.push_back(kIntegerClass);
791 return k8ByteSize;
792 case PTY_f32:
793 classes.push_back(kMemoryClass);
794 return k4ByteSize;
795 case PTY_f64:
796 classes.push_back(kMemoryClass);
797 return k8ByteSize;
798 default:
799 CHECK_FATAL(false, "NYI");
800 }
801 return 0;
802 }
803
InitReturnInfo(MIRType & retTy,CCLocInfo & pLoc)804 void AArch64WebKitJSCC::InitReturnInfo(MIRType &retTy, CCLocInfo &pLoc)
805 {
806 // don't see why this function exisits?
807 LocateRetVal(retTy, pLoc);
808 }
809
SetupSecondRetReg(const MIRType & retTy2,CCLocInfo & pLoc) const810 void AArch64WebKitJSCC::SetupSecondRetReg(const MIRType &retTy2, CCLocInfo &pLoc) const
811 {
812 // already done in locate retval;
813 return;
814 }
815
LocateNextParm(MIRType & mirType,CCLocInfo & pLoc,bool isFirst,MIRFunction * tFunc)816 int32 GHCCC::LocateNextParm(MIRType &mirType, CCLocInfo &pLoc, bool isFirst, MIRFunction *tFunc)
817 {
818 std::vector<ArgumentClass> classes {};
819 int32 alignedTySize = ClassificationArg(beCommon, mirType, classes);
820 pLoc.memSize = alignedTySize;
821 if (classes[0] == kIntegerClass) {
822 if ((alignedTySize == k4ByteSize) || (alignedTySize == k8ByteSize)) {
823 pLoc.reg0 = AllocateGPParmRegister();
824 } else {
825 CHECK_FATAL(false, "no should not go here");
826 }
827 } else if (classes[0] == kFloatClass) {
828 if (alignedTySize == k4ByteSize) {
829 pLoc.reg0 = AllocateSIMDFPParmRegisterF32();
830 } else if (alignedTySize == k8ByteSize) {
831 pLoc.reg0 = AllocateSIMDFPParmRegisterF64();
832 } else if (alignedTySize == k16ByteSize) {
833 pLoc.reg0 = AllocateSIMDFPParmRegisterF128();
834 } else {
835 CHECK_FATAL(false, "no should not go here");
836 }
837 }
838 if (pLoc.reg0 == kRinvalid || classes[0] == kMemoryClass) {
839 /* being passed in memory */
840 CHECK_FATAL(false, "GHC does not support stack pass");
841 }
842 return 0;
843 }
844
LocateRetVal(MIRType & retType,CCLocInfo & pLoc)845 int32 GHCCC::LocateRetVal(MIRType &retType, CCLocInfo &pLoc)
846 {
847 CHECK_FATAL(false, "GHC does not return");
848 return 0;
849 }
850
ClassificationArg(const BECommon & be,MIRType & mirType,std::vector<ArgumentClass> & classes) const851 int32 GHCCC::ClassificationArg(const BECommon &be, MIRType &mirType, std::vector<ArgumentClass> &classes) const
852 {
853 switch (mirType.GetPrimType()) {
854 /*
855 * Arguments of types void, (signed and unsigned) _Bool, char, short, int,
856 * long, long long, and pointers are in the INTEGER class.
857 */
858 case PTY_void:
859 case PTY_u1:
860 case PTY_u8:
861 case PTY_i8:
862 case PTY_u16:
863 case PTY_i16:
864 case PTY_a32:
865 case PTY_u32:
866 case PTY_i32:
867 case PTY_a64:
868 case PTY_ptr:
869 case PTY_ref:
870 case PTY_u64:
871 case PTY_i64:
872 classes.push_back(kIntegerClass);
873 return k8ByteSize;
874 case PTY_f32:
875 classes.push_back(kFloatClass);
876 return k4ByteSize;
877 case PTY_f64:
878 case PTY_v2i32:
879 case PTY_v4i16:
880 case PTY_v8i8:
881 case PTY_v2f32:
882 classes.push_back(kFloatClass);
883 return k8ByteSize;
884 case PTY_v2i64:
885 case PTY_v4i32:
886 case PTY_v8i16:
887 case PTY_v16i8:
888 case PTY_v4f32:
889 case PTY_f128:
890 classes.push_back(kFloatClass);
891 return k16ByteSize;
892 default:
893 CHECK_FATAL(false, "NYI");
894 }
895 return 0;
896 }
897
InitReturnInfo(MIRType & retTy,CCLocInfo & pLoc)898 void GHCCC::InitReturnInfo(MIRType &retTy, CCLocInfo &pLoc)
899 {
900 // don't see why this function exisits?
901 LocateRetVal(retTy, pLoc);
902 }
903
SetupSecondRetReg(const MIRType & retTy2,CCLocInfo & pLoc) const904 void GHCCC::SetupSecondRetReg(const MIRType &retTy2, CCLocInfo &pLoc) const
905 {
906 // already done in locate retval;
907 CHECK_FATAL(false, "GHC does not return");
908 return;
909 }
910 /*
911 * From "ARM Procedure Call Standard for ARM 64-bit Architecture"
912 * ARM IHI 0055C_beta, 6th November 2013
913 * $ 5.1 machine Registers
914 * $ 5.1.1 General-Purpose Registers
915 * <Table 2> Note
916 * SP Stack Pointer
917 * R30/LR Link register Stores the return address.
918 * We push it into stack along with FP on function
919 * entry using STP and restore it on function exit
920 * using LDP even if the function is a leaf (i.e.,
921 * it does not call any other function) because it
922 * is free (we have to store FP anyway). So, if a
923 * function is a leaf, we may use it as a temporary
924 * register.
925 * R29/FP Frame Pointer
926 * R19-R28 Callee-saved
927 * registers
928 * R18 Platform reg Can we use it as a temporary register?
929 * R16,R17 IP0,IP1 Maybe used as temporary registers. Should be
930 * given lower priorities. (i.e., we push them
931 * into the free register stack before the others)
932 * R9-R15 Temporary registers, caller-saved
933 * Note:
934 * R16 and R17 may be used by a linker as a scratch register between
935 * a routine and any subroutine it calls. They can also be used within a
936 * routine to hold intermediate values between subroutine calls.
937 *
938 * The role of R18 is platform specific. If a platform ABI has need of
939 * a dedicated general purpose register to carry inter-procedural state
940 * (for example, the thread context) then it should use this register for
941 * that purpose. If the platform ABI has no such requirements, then it should
942 * use R18 as an additional temporary register. The platform ABI specification
943 * must document the usage for this register.
944 *
945 * A subroutine invocation must preserve the contents of the registers R19-R29
946 * and SP. All 64 bits of each value stored in R19-R29 must be preserved, even
947 * when using the ILP32 data model.
948 *
949 * $ 5.1.2 SIMD and Floating-Point Registers
950 *
951 * The first eight registers, V0-V7, are used to pass argument values into
952 * a subroutine and to return result values from a function. They may also
953 * be used to hold intermediate values within a routine.
954 *
955 * V8-V15 must be preserved by a callee across subroutine calls; the
956 * remaining registers do not need to be preserved( or caller-saved).
957 * Additionally, only the bottom 64 bits of each value stored in V8-
958 * V15 need to be preserved.
959 */
960 } /* namespace maplebe */
961