• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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