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_cg.h"
17 #include "aarch64_cgfunc.h"
18 #include <vector>
19 #include <cstdint>
20 #include <sys/stat.h>
21 #include <atomic>
22 #include "cfi.h"
23 #include "mpl_logging.h"
24 #include "rt.h"
25 #include "opcode_info.h"
26 #include "mir_builder.h"
27 #include "mir_symbol_builder.h"
28 #include "mpl_atomic.h"
29 #include "metadata_layout.h"
30 #include "emit.h"
31 #include "simplify.h"
32 #include <algorithm>
33
34 namespace maplebe {
35 using namespace maple;
36 CondOperand AArch64CGFunc::ccOperands[kCcLast] = {
37 CondOperand(CC_EQ), CondOperand(CC_NE), CondOperand(CC_CS), CondOperand(CC_HS), CondOperand(CC_CC),
38 CondOperand(CC_LO), CondOperand(CC_MI), CondOperand(CC_PL), CondOperand(CC_VS), CondOperand(CC_VC),
39 CondOperand(CC_HI), CondOperand(CC_LS), CondOperand(CC_GE), CondOperand(CC_LT), CondOperand(CC_GT),
40 CondOperand(CC_LE), CondOperand(CC_AL),
41 };
42
43 namespace {
44 constexpr int32 kSignedDimension = 2; /* signed and unsigned */
45 constexpr int32 kIntByteSizeDimension = 4; /* 1 byte, 2 byte, 4 bytes, 8 bytes */
46 constexpr int32 kFloatByteSizeDimension = 3; /* 4 bytes, 8 bytes, 16 bytes(vector) */
47 constexpr int32 kShiftAmount12 = 12; /* for instruction that can use shift, shift amount must be 0 or 12 */
48
49 MOperator ldIs[kSignedDimension][kIntByteSizeDimension] = {
50 /* unsigned == 0 */
51 {MOP_wldrb, MOP_wldrh, MOP_wldr, MOP_xldr},
52 /* signed == 1 */
53 {MOP_wldrsb, MOP_wldrsh, MOP_wldr, MOP_xldr}};
54
55 MOperator stIs[kSignedDimension][kIntByteSizeDimension] = {
56 /* unsigned == 0 */
57 {MOP_wstrb, MOP_wstrh, MOP_wstr, MOP_xstr},
58 /* signed == 1 */
59 {MOP_wstrb, MOP_wstrh, MOP_wstr, MOP_xstr}};
60
61 MOperator ldIsAcq[kSignedDimension][kIntByteSizeDimension] = {
62 /* unsigned == 0 */
63 {MOP_wldarb, MOP_wldarh, MOP_wldar, MOP_xldar},
64 /* signed == 1 */
65 {MOP_wldarb, MOP_wldarh, MOP_wldar, MOP_xldar}};
66
67 MOperator stIsRel[kSignedDimension][kIntByteSizeDimension] = {
68 /* unsigned == 0 */
69 {MOP_wstlrb, MOP_wstlrh, MOP_wstlr, MOP_xstlr},
70 /* signed == 1 */
71 {MOP_wstlrb, MOP_wstlrh, MOP_wstlr, MOP_xstlr}};
72
73 MOperator ldFs[kFloatByteSizeDimension] = {MOP_sldr, MOP_dldr, MOP_qldr};
74 MOperator stFs[kFloatByteSizeDimension] = {MOP_sstr, MOP_dstr, MOP_qstr};
75
76 MOperator ldFsAcq[kFloatByteSizeDimension] = {MOP_undef, MOP_undef, MOP_undef};
77 MOperator stFsRel[kFloatByteSizeDimension] = {MOP_undef, MOP_undef, MOP_undef};
78
79 /* extended to unsigned ints */
80 MOperator uextIs[kIntByteSizeDimension][kIntByteSizeDimension] = {
81 /* u8 u16 u32 u64 */
82 {MOP_undef, MOP_xuxtb32, MOP_xuxtb32, MOP_xuxtb32}, /* u8/i8 */
83 {MOP_undef, MOP_undef, MOP_xuxth32, MOP_xuxth32}, /* u16/i16 */
84 {MOP_undef, MOP_undef, MOP_xuxtw64, MOP_xuxtw64}, /* u32/i32 */
85 {MOP_undef, MOP_undef, MOP_undef, MOP_undef} /* u64/u64 */
86 };
87
88 /* extended to signed ints */
89 MOperator extIs[kIntByteSizeDimension][kIntByteSizeDimension] = {
90 /* i8 i16 i32 i64 */
91 {MOP_undef, MOP_xsxtb32, MOP_xsxtb32, MOP_xsxtb64}, /* u8/i8 */
92 {MOP_undef, MOP_undef, MOP_xsxth32, MOP_xsxth64}, /* u16/i16 */
93 {MOP_undef, MOP_undef, MOP_undef, MOP_xsxtw64}, /* u32/i32 */
94 {MOP_undef, MOP_undef, MOP_undef, MOP_undef} /* u64/u64 */
95 };
96
PickLdStInsn(bool isLoad,uint32 bitSize,PrimType primType,AArch64isa::MemoryOrdering memOrd)97 MOperator PickLdStInsn(bool isLoad, uint32 bitSize, PrimType primType, AArch64isa::MemoryOrdering memOrd)
98 {
99 DEBUG_ASSERT(__builtin_popcount(static_cast<uint32>(memOrd)) <= 1, "must be kMoNone or kMoAcquire");
100 DEBUG_ASSERT(bitSize >= k8BitSize, "PTY_u1 should have been lowered?");
101 DEBUG_ASSERT(__builtin_popcount(bitSize) == 1, "PTY_u1 should have been lowered?");
102 if (isLoad) {
103 DEBUG_ASSERT((memOrd == AArch64isa::kMoNone) || (memOrd == AArch64isa::kMoAcquire) ||
104 (memOrd == AArch64isa::kMoAcquireRcpc) || (memOrd == AArch64isa::kMoLoacquire),
105 "unknown Memory Order");
106 } else {
107 DEBUG_ASSERT((memOrd == AArch64isa::kMoNone) || (memOrd == AArch64isa::kMoRelease) ||
108 (memOrd == AArch64isa::kMoLorelease),
109 "unknown Memory Order");
110 }
111
112 /* __builtin_ffs(x) returns: 0 -> 0, 1 -> 1, 2 -> 2, 4 -> 3, 8 -> 4 */
113 if ((IsPrimitiveInteger(primType) || primType == PTY_agg) && !IsPrimitiveVector(primType)) {
114 MOperator(*table)[kIntByteSizeDimension];
115 if (isLoad) {
116 table = (memOrd == AArch64isa::kMoAcquire) ? ldIsAcq : ldIs;
117 } else {
118 table = (memOrd == AArch64isa::kMoRelease) ? stIsRel : stIs;
119 }
120
121 int32 signedUnsigned = IsUnsignedInteger(primType) ? 0 : 1;
122 if (primType == PTY_agg) {
123 CHECK_FATAL(bitSize >= k8BitSize, " unexpect agg size");
124 bitSize = static_cast<uint32>(RoundUp(bitSize, k8BitSize));
125 DEBUG_ASSERT((bitSize & (bitSize - 1)) == 0, "bitlen error");
126 }
127
128 /* __builtin_ffs(x) returns: 8 -> 4, 16 -> 5, 32 -> 6, 64 -> 7 */
129 if (primType == PTY_i128 || primType == PTY_u128) {
130 bitSize = k64BitSize;
131 }
132 uint32 size = static_cast<uint32>(__builtin_ffs(static_cast<int32>(bitSize))) - k4BitSize;
133 DEBUG_ASSERT(size <= 3, "wrong bitSize"); // size must <= 3
134 return table[signedUnsigned][size];
135 } else {
136 MOperator *table = nullptr;
137 if (isLoad) {
138 table = (memOrd == AArch64isa::kMoAcquire) ? ldFsAcq : ldFs;
139 } else {
140 table = (memOrd == AArch64isa::kMoRelease) ? stFsRel : stFs;
141 }
142
143 /* __builtin_ffs(x) returns: 32 -> 6, 64 -> 7, 128 -> 8 */
144 uint32 size = static_cast<uint32>(__builtin_ffs(static_cast<int32>(bitSize))) - k6BitSize;
145 DEBUG_ASSERT(size <= k2BitSize, "size must be 0 to 2");
146 return table[size];
147 }
148 }
149 } // namespace
150
IsBlkassignForPush(const BlkassignoffNode & bNode)151 bool IsBlkassignForPush(const BlkassignoffNode &bNode)
152 {
153 BaseNode *dest = bNode.Opnd(0);
154 bool spBased = false;
155 if (dest->GetOpCode() == OP_regread) {
156 RegreadNode &node = static_cast<RegreadNode &>(*dest);
157 if (-node.GetRegIdx() == kSregSp) {
158 spBased = true;
159 }
160 }
161 return spBased;
162 }
163
SetMemReferenceOfInsn(Insn & insn,BaseNode * baseNode)164 void AArch64CGFunc::SetMemReferenceOfInsn(Insn &insn, BaseNode *baseNode)
165 {
166 if (baseNode == nullptr) {
167 return;
168 }
169 MIRFunction &mirFunction = GetFunction();
170 MemReferenceTable *memRefTable = mirFunction.GetMemReferenceTable();
171 // In O0 mode, we do not run the ME, and the referenceTable is nullptr
172 if (memRefTable == nullptr) {
173 return;
174 }
175 MemDefUsePart &memDefUsePart = memRefTable->GetMemDefUsePart();
176 if (memDefUsePart.find(baseNode) != memDefUsePart.end()) {
177 insn.SetReferenceOsts(memDefUsePart.find(baseNode)->second);
178 }
179 }
180
GetLmbcStructArgType(BaseNode & stmt,size_t argNo) const181 MIRStructType *AArch64CGFunc::GetLmbcStructArgType(BaseNode &stmt, size_t argNo) const
182 {
183 MIRType *ty = nullptr;
184 if (stmt.GetOpCode() == OP_call) {
185 CallNode &callNode = static_cast<CallNode &>(stmt);
186 MIRFunction *callFunc = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(callNode.GetPUIdx());
187 if (callFunc->GetFormalCount() < (argNo + 1UL)) {
188 return nullptr; /* formals less than actuals */
189 }
190 ty = GlobalTables::GetTypeTable().GetTypeFromTyIdx(callFunc->GetFormalDefVec()[argNo].formalTyIdx);
191 } else if (stmt.GetOpCode() == OP_icallproto) {
192 argNo--; /* 1st opnd of icallproto is funcname, skip it relative to param list */
193 IcallNode &icallproto = static_cast<IcallNode &>(stmt);
194 MIRType *type = GlobalTables::GetTypeTable().GetTypeFromTyIdx(icallproto.GetRetTyIdx());
195 MIRFuncType *fType = nullptr;
196 if (type->IsMIRPtrType()) {
197 fType = static_cast<MIRPtrType *>(type)->GetPointedFuncType();
198 } else {
199 fType = static_cast<MIRFuncType *>(type);
200 }
201 CHECK_FATAL(fType != nullptr, "invalid fType");
202 if (fType->GetParamTypeList().size() < (argNo + 1UL)) {
203 return nullptr;
204 }
205 ty = GlobalTables::GetTypeTable().GetTypeFromTyIdx(fType->GetNthParamType(argNo));
206 }
207 CHECK_FATAL(ty && ty->IsStructType(), "lmbc agg arg error");
208 return static_cast<MIRStructType *>(ty);
209 }
210
GetOrCreateResOperand(const BaseNode & parent,PrimType primType)211 RegOperand &AArch64CGFunc::GetOrCreateResOperand(const BaseNode &parent, PrimType primType)
212 {
213 RegOperand *resOpnd = nullptr;
214 if (parent.GetOpCode() == OP_regassign) {
215 auto ®AssignNode = static_cast<const RegassignNode &>(parent);
216 PregIdx pregIdx = regAssignNode.GetRegIdx();
217 if (IsSpecialPseudoRegister(pregIdx)) {
218 /* if it is one of special registers */
219 resOpnd = &GetOrCreateSpecialRegisterOperand(-pregIdx, primType);
220 } else {
221 resOpnd = &GetOrCreateVirtualRegisterOperand(GetVirtualRegNOFromPseudoRegIdx(pregIdx));
222 }
223 } else {
224 resOpnd = &CreateRegisterOperandOfType(primType);
225 }
226 return *resOpnd;
227 }
228
PickLdInsn(uint32 bitSize,PrimType primType,AArch64isa::MemoryOrdering memOrd) const229 MOperator AArch64CGFunc::PickLdInsn(uint32 bitSize, PrimType primType, AArch64isa::MemoryOrdering memOrd) const
230 {
231 return PickLdStInsn(true, bitSize, primType, memOrd);
232 }
233
PickStInsn(uint32 bitSize,PrimType primType,AArch64isa::MemoryOrdering memOrd) const234 MOperator AArch64CGFunc::PickStInsn(uint32 bitSize, PrimType primType, AArch64isa::MemoryOrdering memOrd) const
235 {
236 return PickLdStInsn(false, bitSize, primType, memOrd);
237 }
238
PickExtInsn(PrimType dtype,PrimType stype) const239 MOperator AArch64CGFunc::PickExtInsn(PrimType dtype, PrimType stype) const
240 {
241 int32 sBitSize = static_cast<int32>(GetPrimTypeBitSize(stype));
242 int32 dBitSize = static_cast<int32>(GetPrimTypeBitSize(dtype));
243 /* __builtin_ffs(x) returns: 0 -> 0, 1 -> 1, 2 -> 2, 4 -> 3, 8 -> 4 */
244 if (IsPrimitiveInteger(stype) && IsPrimitiveInteger(dtype)) {
245 MOperator(*table)[kIntByteSizeDimension];
246 table = IsUnsignedInteger(stype) ? uextIs : extIs;
247 if (stype == PTY_i128 || stype == PTY_u128) {
248 sBitSize = static_cast<int32>(k64BitSize);
249 }
250 /* __builtin_ffs(x) returns: 8 -> 4, 16 -> 5, 32 -> 6, 64 -> 7 */
251 uint32 row = static_cast<uint32>(__builtin_ffs(sBitSize)) - k4BitSize;
252 DEBUG_ASSERT(row <= k3BitSize, "wrong bitSize");
253 if (dtype == PTY_i128 || dtype == PTY_u128) {
254 dBitSize = static_cast<int32>(k64BitSize);
255 }
256 uint32 col = static_cast<uint32>(__builtin_ffs(dBitSize)) - k4BitSize;
257 DEBUG_ASSERT(col <= k3BitSize, "wrong bitSize");
258 return table[row][col];
259 }
260 CHECK_FATAL(0, "extend not primitive integer");
261 return MOP_undef;
262 }
263
PickMovBetweenRegs(PrimType destType,PrimType srcType) const264 MOperator AArch64CGFunc::PickMovBetweenRegs(PrimType destType, PrimType srcType) const
265 {
266 if (IsPrimitiveVector(destType) && IsPrimitiveVector(srcType)) {
267 return GetPrimTypeSize(srcType) == k8ByteSize ? MOP_vmovuu : MOP_vmovvv;
268 }
269 if (IsPrimitiveInteger(destType) && IsPrimitiveInteger(srcType)) {
270 return GetPrimTypeSize(srcType) <= k4ByteSize ? MOP_wmovrr : MOP_xmovrr;
271 }
272 if (IsPrimitiveFloat(destType) && IsPrimitiveFloat(srcType)) {
273 return GetPrimTypeSize(srcType) <= k4ByteSize ? MOP_xvmovs : MOP_xvmovd;
274 }
275 if (IsPrimitiveInteger(destType) && IsPrimitiveFloat(srcType)) {
276 return GetPrimTypeSize(srcType) <= k4ByteSize ? MOP_xvmovrs : MOP_xvmovrd;
277 }
278 if (IsPrimitiveFloat(destType) && IsPrimitiveInteger(srcType)) {
279 return GetPrimTypeSize(srcType) <= k4ByteSize ? MOP_xvmovsr : MOP_xvmovdr;
280 }
281 if (IsPrimitiveInteger(destType) && IsPrimitiveVector(srcType)) {
282 return GetPrimTypeSize(srcType) == k8ByteSize
283 ? MOP_vwmovru
284 : GetPrimTypeSize(destType) <= k4ByteSize ? MOP_vwmovrv : MOP_vxmovrv;
285 }
286 CHECK_FATAL(false, "unexpected operand primtype for mov");
287 return MOP_undef;
288 }
289
PickMovInsn(const RegOperand & lhs,const RegOperand & rhs) const290 MOperator AArch64CGFunc::PickMovInsn(const RegOperand &lhs, const RegOperand &rhs) const
291 {
292 CHECK_FATAL(lhs.GetRegisterType() == rhs.GetRegisterType(), "PickMovInsn: unequal kind NYI");
293 CHECK_FATAL(lhs.GetSize() == rhs.GetSize(), "PickMovInsn: unequal size NYI");
294 DEBUG_ASSERT(((lhs.GetSize() < k64BitSize) || (lhs.GetRegisterType() == kRegTyFloat)),
295 "should split the 64 bits or more mov");
296 if (lhs.GetRegisterType() == kRegTyInt) {
297 return MOP_wmovrr;
298 }
299 if (lhs.GetRegisterType() == kRegTyFloat) {
300 return (lhs.GetSize() <= k32BitSize) ? MOP_xvmovs : MOP_xvmovd;
301 }
302 DEBUG_ASSERT(false, "PickMovInsn: kind NYI");
303 return MOP_undef;
304 }
305
SelectLoadAcquire(Operand & dest,PrimType dtype,Operand & src,PrimType stype,AArch64isa::MemoryOrdering memOrd,bool isDirect)306 void AArch64CGFunc::SelectLoadAcquire(Operand &dest, PrimType dtype, Operand &src, PrimType stype,
307 AArch64isa::MemoryOrdering memOrd, bool isDirect)
308 {
309 DEBUG_ASSERT(src.GetKind() == Operand::kOpdMem, "Just checking");
310 DEBUG_ASSERT(memOrd != AArch64isa::kMoNone, "Just checking");
311
312 uint32 ssize = isDirect ? src.GetSize() : GetPrimTypeBitSize(dtype);
313 uint32 dsize = GetPrimTypeBitSize(dtype);
314 MOperator mOp = PickLdInsn(ssize, stype, memOrd);
315
316 Operand *newSrc = &src;
317 auto &memOpnd = static_cast<MemOperand &>(src);
318 OfstOperand *immOpnd = memOpnd.GetOffsetImmediate();
319 int32 offset = static_cast<int32>(immOpnd->GetOffsetValue());
320 RegOperand *origBaseReg = memOpnd.GetBaseRegister();
321 if (offset != 0) {
322 RegOperand &resOpnd = CreateRegisterOperandOfType(PTY_i64);
323 DEBUG_ASSERT(origBaseReg != nullptr, "nullptr check");
324 SelectAdd(resOpnd, *origBaseReg, *immOpnd, PTY_i64);
325 newSrc = &CreateReplacementMemOperand(ssize, resOpnd, 0);
326 }
327
328 std::string key;
329 if (isDirect && GetCG()->GenerateVerboseCG()) {
330 key = GenerateMemOpndVerbose(src);
331 }
332
333 /* Check if the right load-acquire instruction is available. */
334 if (mOp != MOP_undef) {
335 Insn &insn = GetInsnBuilder()->BuildInsn(mOp, dest, *newSrc);
336 if (isDirect && GetCG()->GenerateVerboseCG()) {
337 insn.SetComment(key);
338 }
339 GetCurBB()->AppendInsn(insn);
340 } else {
341 if (IsPrimitiveFloat(stype)) {
342 /* Uses signed integer version ldar followed by a floating-point move(fmov). */
343 DEBUG_ASSERT(stype == dtype, "Just checking");
344 PrimType itype = (stype == PTY_f32) ? PTY_i32 : PTY_i64;
345 RegOperand ®Opnd = CreateRegisterOperandOfType(itype);
346 Insn &insn = GetInsnBuilder()->BuildInsn(PickLdInsn(ssize, itype, memOrd), regOpnd, *newSrc);
347 if (isDirect && GetCG()->GenerateVerboseCG()) {
348 insn.SetComment(key);
349 }
350 GetCurBB()->AppendInsn(insn);
351 mOp = (stype == PTY_f32) ? MOP_xvmovsr : MOP_xvmovdr;
352 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, dest, regOpnd));
353 } else {
354 /* Use unsigned version ldarb/ldarh followed by a sign-extension instruction(sxtb/sxth). */
355 DEBUG_ASSERT((ssize == k8BitSize) || (ssize == k16BitSize), "Just checking");
356 PrimType utype = (ssize == k8BitSize) ? PTY_u8 : PTY_u16;
357 Insn &insn = GetInsnBuilder()->BuildInsn(PickLdInsn(ssize, utype, memOrd), dest, *newSrc);
358 if (isDirect && GetCG()->GenerateVerboseCG()) {
359 insn.SetComment(key);
360 }
361 GetCurBB()->AppendInsn(insn);
362 mOp = ((dsize == k32BitSize) ? ((ssize == k8BitSize) ? MOP_xsxtb32 : MOP_xsxth32)
363 : ((ssize == k8BitSize) ? MOP_xsxtb64 : MOP_xsxth64));
364 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, dest, dest));
365 }
366 }
367 }
368
SelectStoreRelease(Operand & dest,PrimType dtype,Operand & src,PrimType stype,AArch64isa::MemoryOrdering memOrd,bool isDirect)369 void AArch64CGFunc::SelectStoreRelease(Operand &dest, PrimType dtype, Operand &src, PrimType stype,
370 AArch64isa::MemoryOrdering memOrd, bool isDirect)
371 {
372 DEBUG_ASSERT(dest.GetKind() == Operand::kOpdMem, "Just checking");
373
374 uint32 dsize = isDirect ? dest.GetSize() : GetPrimTypeBitSize(stype);
375 MOperator mOp = PickStInsn(dsize, stype, memOrd);
376
377 Operand *newDest = &dest;
378 MemOperand *memOpnd = static_cast<MemOperand *>(&dest);
379 OfstOperand *immOpnd = memOpnd->GetOffsetImmediate();
380 int32 offset = static_cast<int32>(immOpnd->GetOffsetValue());
381 RegOperand *origBaseReg = memOpnd->GetBaseRegister();
382 if (offset != 0) {
383 RegOperand &resOpnd = CreateRegisterOperandOfType(PTY_i64);
384 DEBUG_ASSERT(origBaseReg != nullptr, "nullptr check");
385 SelectAdd(resOpnd, *origBaseReg, *immOpnd, PTY_i64);
386 newDest = &CreateReplacementMemOperand(dsize, resOpnd, 0);
387 }
388
389 std::string key;
390 if (isDirect && GetCG()->GenerateVerboseCG()) {
391 key = GenerateMemOpndVerbose(dest);
392 }
393
394 /* Check if the right store-release instruction is available. */
395 if (mOp != MOP_undef) {
396 Insn &insn = GetInsnBuilder()->BuildInsn(mOp, src, *newDest);
397 if (isDirect && GetCG()->GenerateVerboseCG()) {
398 insn.SetComment(key);
399 }
400 GetCurBB()->AppendInsn(insn);
401 } else {
402 /* Use a floating-point move(fmov) followed by a stlr. */
403 DEBUG_ASSERT(IsPrimitiveFloat(stype), "must be float type");
404 CHECK_FATAL(stype == dtype, "Just checking");
405 PrimType itype = (stype == PTY_f32) ? PTY_i32 : PTY_i64;
406 RegOperand ®Opnd = CreateRegisterOperandOfType(itype);
407 mOp = (stype == PTY_f32) ? MOP_xvmovrs : MOP_xvmovrd;
408 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, regOpnd, src));
409 Insn &insn = GetInsnBuilder()->BuildInsn(PickStInsn(dsize, itype, memOrd), regOpnd, *newDest);
410 if (isDirect && GetCG()->GenerateVerboseCG()) {
411 insn.SetComment(key);
412 }
413 GetCurBB()->AppendInsn(insn);
414 }
415 }
416
SelectCopyImm(Operand & dest,PrimType dType,ImmOperand & src,PrimType sType)417 void AArch64CGFunc::SelectCopyImm(Operand &dest, PrimType dType, ImmOperand &src, PrimType sType)
418 {
419 if (IsPrimitiveInteger(dType) != IsPrimitiveInteger(sType)) {
420 RegOperand &tempReg = CreateRegisterOperandOfType(sType);
421 SelectCopyImm(tempReg, src, sType);
422 SelectCopy(dest, dType, tempReg, sType);
423 } else {
424 SelectCopyImm(dest, src, sType);
425 }
426 }
427
SelectCopyImm(Operand & dest,ImmOperand & src,PrimType dtype)428 void AArch64CGFunc::SelectCopyImm(Operand &dest, ImmOperand &src, PrimType dtype)
429 {
430 uint32 dsize = GetPrimTypeBitSize(dtype);
431 // If the type size of the parent node is smaller than the type size of the child node,
432 // the number of child node needs to be truncated.
433 if (dsize < src.GetSize()) {
434 uint64 value = static_cast<uint64>(src.GetValue());
435 uint64 mask = (1UL << dsize) - 1;
436 int64 newValue = static_cast<int64>(value & mask);
437 src.SetValue(newValue);
438 }
439 DEBUG_ASSERT(IsPrimitiveInteger(dtype), "The type of destination operand must be Integer");
440 DEBUG_ASSERT(((dsize == k8BitSize) || (dsize == k16BitSize) || (dsize == k32BitSize) || (dsize == k64BitSize)),
441 "The destination operand must be >= 8-bit");
442 if (src.GetSize() == k32BitSize && dsize == k64BitSize && src.IsSingleInstructionMovable()) {
443 auto tempReg = CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k32BitSize), k32BitSize, kRegTyInt);
444 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wmovri32, *tempReg, src));
445 SelectCopy(dest, dtype, *tempReg, PTY_u32);
446 return;
447 }
448 if (src.IsSingleInstructionMovable()) {
449 MOperator mOp = (dsize <= k32BitSize) ? MOP_wmovri32 : MOP_xmovri64;
450 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, dest, src));
451 return;
452 }
453 uint64 srcVal = static_cast<uint64>(src.GetValue());
454 /* using mov/movk to load the immediate value */
455 if (dsize == k8BitSize) {
456 /* compute lower 8 bits value */
457 if (dtype == PTY_u8) {
458 /* zero extend */
459 srcVal = (srcVal << k56BitSize) >> k56BitSize;
460 dtype = PTY_u16;
461 } else {
462 /* sign extend */
463 srcVal = (static_cast<int64>(srcVal) << k56BitSize) >> k56BitSize;
464 dtype = PTY_i16;
465 }
466 dsize = k16BitSize;
467 }
468 if (dsize == k16BitSize) {
469 if (dtype == PTY_u16) {
470 /* check lower 16 bits and higher 16 bits respectively */
471 DEBUG_ASSERT((srcVal & 0x0000FFFFULL) != 0, "unexpected value");
472 DEBUG_ASSERT(((srcVal >> k16BitSize) & 0x0000FFFFULL) == 0, "unexpected value");
473 DEBUG_ASSERT((srcVal & 0x0000FFFFULL) != 0xFFFFULL, "unexpected value");
474 /* create an imm opereand which represents lower 16 bits of the immediate */
475 ImmOperand &srcLower = CreateImmOperand(static_cast<int64>(srcVal & 0x0000FFFFULL), k16BitSize, false);
476 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wmovri32, dest, srcLower));
477 return;
478 } else {
479 /* sign extend and let `dsize == 32` case take care of it */
480 srcVal = (static_cast<int64>(srcVal) << k48BitSize) >> k48BitSize;
481 dsize = k32BitSize;
482 }
483 }
484 if (dsize == k32BitSize) {
485 /* check lower 16 bits and higher 16 bits respectively */
486 DEBUG_ASSERT((srcVal & 0x0000FFFFULL) != 0, "unexpected val");
487 DEBUG_ASSERT(((srcVal >> k16BitSize) & 0x0000FFFFULL) != 0, "unexpected val");
488 DEBUG_ASSERT((srcVal & 0x0000FFFFULL) != 0xFFFFULL, "unexpected val");
489 DEBUG_ASSERT(((srcVal >> k16BitSize) & 0x0000FFFFULL) != 0xFFFFULL, "unexpected val");
490 /* create an imm opereand which represents lower 16 bits of the immediate */
491 ImmOperand &srcLower = CreateImmOperand(static_cast<int64>(srcVal & 0x0000FFFFULL), k16BitSize, false);
492 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wmovri32, dest, srcLower));
493 /* create an imm opereand which represents upper 16 bits of the immediate */
494 ImmOperand &srcUpper =
495 CreateImmOperand(static_cast<int64>((srcVal >> k16BitSize) & 0x0000FFFFULL), k16BitSize, false);
496 BitShiftOperand *lslOpnd = GetLogicalShiftLeftOperand(k16BitSize, false);
497 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wmovkri16, dest, srcUpper, *lslOpnd));
498 } else {
499 /*
500 * partition it into 4 16-bit chunks
501 * if more 0's than 0xFFFF's, use movz as the initial instruction.
502 * otherwise, movn.
503 */
504 bool useMovz = BetterUseMOVZ(srcVal);
505 bool useMovk = false;
506 /* get lower 32 bits of the immediate */
507 uint64 chunkLval = srcVal & 0xFFFFFFFFULL;
508 /* get upper 32 bits of the immediate */
509 uint64 chunkHval = (srcVal >> k32BitSize) & 0xFFFFFFFFULL;
510 int32 maxLoopTime = 4;
511
512 if (chunkLval == chunkHval) {
513 /* compute lower 32 bits, and then copy to higher 32 bits, so only 2 chunks need be processed */
514 maxLoopTime = 2;
515 }
516
517 uint64 sa = 0;
518
519 for (int64 i = 0; i < maxLoopTime; ++i, sa += k16BitSize) {
520 /* create an imm opereand which represents the i-th 16-bit chunk of the immediate */
521 uint64 chunkVal = (srcVal >> (static_cast<uint64>(sa))) & 0x0000FFFFULL;
522 if (useMovz ? (chunkVal == 0) : (chunkVal == 0x0000FFFFULL)) {
523 continue;
524 }
525 ImmOperand &src16 = CreateImmOperand(static_cast<int64>(chunkVal), k16BitSize, false);
526 BitShiftOperand *lslOpnd = GetLogicalShiftLeftOperand(sa, true);
527 if (!useMovk) {
528 /* use movz or movn */
529 if (!useMovz) {
530 src16.BitwiseNegate();
531 }
532 GetCurBB()->AppendInsn(
533 GetInsnBuilder()->BuildInsn(useMovz ? MOP_xmovzri16 : MOP_xmovnri16, dest, src16, *lslOpnd));
534 useMovk = true;
535 } else {
536 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xmovkri16, dest, src16, *lslOpnd));
537 }
538 }
539
540 if (maxLoopTime == 2) { /* as described above, only 2 chunks need be processed */
541 /* copy lower 32 bits to higher 32 bits */
542 ImmOperand &immOpnd = CreateImmOperand(k32BitSize, k8BitSize, false);
543 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xbfirri6i6, dest, dest, immOpnd, immOpnd));
544 }
545 }
546 }
547
GenerateMemOpndVerbose(const Operand & src) const548 std::string AArch64CGFunc::GenerateMemOpndVerbose(const Operand &src) const
549 {
550 DEBUG_ASSERT(src.GetKind() == Operand::kOpdMem, "Just checking");
551 const MIRSymbol *symSecond = static_cast<const MemOperand *>(&src)->GetSymbol();
552 if (symSecond != nullptr) {
553 std::string key;
554 MIRStorageClass sc = symSecond->GetStorageClass();
555 if (sc == kScFormal) {
556 key = "param: ";
557 } else if (sc == kScAuto) {
558 key = "local var: ";
559 } else {
560 key = "global: ";
561 }
562 key += symSecond->GetName();
563 return key;
564 }
565 return "";
566 }
567
SelectCopyMemOpnd(Operand & dest,PrimType dtype,uint32 dsize,Operand & src,PrimType stype)568 void AArch64CGFunc::SelectCopyMemOpnd(Operand &dest, PrimType dtype, uint32 dsize, Operand &src, PrimType stype)
569 {
570 AArch64isa::MemoryOrdering memOrd = AArch64isa::kMoNone;
571 const MIRSymbol *sym = static_cast<MemOperand *>(&src)->GetSymbol();
572 if ((sym != nullptr) && (sym->GetStorageClass() == kScGlobal) && sym->GetAttr(ATTR_memory_order_acquire)) {
573 memOrd = AArch64isa::kMoAcquire;
574 }
575
576 if (memOrd != AArch64isa::kMoNone) {
577 AArch64CGFunc::SelectLoadAcquire(dest, dtype, src, stype, memOrd, true);
578 return;
579 }
580 Insn *insn = nullptr;
581 uint32 ssize = src.GetSize();
582 PrimType regTy = PTY_void;
583 RegOperand *loadReg = nullptr;
584 MOperator mop = MOP_undef;
585 if (IsPrimitiveFloat(stype) || IsPrimitiveVector(stype)) {
586 CHECK_FATAL(dsize == ssize, "dsize %u expect equals ssize %u", dtype, ssize);
587 insn = &GetInsnBuilder()->BuildInsn(PickLdInsn(ssize, stype), dest, src);
588 } else {
589 if (stype == PTY_agg && dtype == PTY_agg) {
590 mop = MOP_undef;
591 } else {
592 mop = PickExtInsn(dtype, stype);
593 }
594 if (ssize == (GetPrimTypeSize(dtype) * kBitsPerByte) || mop == MOP_undef) {
595 insn = &GetInsnBuilder()->BuildInsn(PickLdInsn(ssize, stype), dest, src);
596 } else {
597 regTy = dsize == k64BitSize ? dtype : PTY_i32;
598 loadReg = &CreateRegisterOperandOfType(regTy);
599 insn = &GetInsnBuilder()->BuildInsn(PickLdInsn(ssize, stype), *loadReg, src);
600 }
601 }
602
603 if (GetCG()->GenerateVerboseCG()) {
604 insn->SetComment(GenerateMemOpndVerbose(src));
605 }
606
607 GetCurBB()->AppendInsn(*insn);
608 if (regTy != PTY_void && mop != MOP_undef) {
609 DEBUG_ASSERT(loadReg != nullptr, "loadReg should not be nullptr");
610 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mop, dest, *loadReg));
611 }
612 }
613
IsImmediateValueInRange(MOperator mOp,int64 immVal,bool is64Bits,bool isIntactIndexed,bool isPostIndexed,bool isPreIndexed) const614 bool AArch64CGFunc::IsImmediateValueInRange(MOperator mOp, int64 immVal, bool is64Bits, bool isIntactIndexed,
615 bool isPostIndexed, bool isPreIndexed) const
616 {
617 bool isInRange = false;
618 switch (mOp) {
619 case MOP_xstr:
620 case MOP_wstr:
621 isInRange =
622 (isIntactIndexed &&
623 ((!is64Bits && (immVal >= kStrAllLdrAllImmLowerBound) && (immVal <= kStrLdrImm32UpperBound)) ||
624 (is64Bits && (immVal >= kStrAllLdrAllImmLowerBound) && (immVal <= kStrLdrImm64UpperBound)))) ||
625 ((isPostIndexed || isPreIndexed) && (immVal >= kStrLdrPerPostLowerBound) &&
626 (immVal <= kStrLdrPerPostUpperBound));
627 break;
628 case MOP_wstrb:
629 isInRange =
630 (isIntactIndexed && (immVal >= kStrAllLdrAllImmLowerBound) && (immVal <= kStrbLdrbImmUpperBound)) ||
631 ((isPostIndexed || isPreIndexed) && (immVal >= kStrLdrPerPostLowerBound) &&
632 (immVal <= kStrLdrPerPostUpperBound));
633 break;
634 case MOP_wstrh:
635 isInRange =
636 (isIntactIndexed && (immVal >= kStrAllLdrAllImmLowerBound) && (immVal <= kStrhLdrhImmUpperBound)) ||
637 ((isPostIndexed || isPreIndexed) && (immVal >= kStrLdrPerPostLowerBound) &&
638 (immVal <= kStrLdrPerPostUpperBound));
639 break;
640 default:
641 break;
642 }
643 return isInRange;
644 }
645
IsStoreMop(MOperator mOp) const646 bool AArch64CGFunc::IsStoreMop(MOperator mOp) const
647 {
648 switch (mOp) {
649 case MOP_sstr:
650 case MOP_dstr:
651 case MOP_qstr:
652 case MOP_xstr:
653 case MOP_wstr:
654 case MOP_wstrb:
655 case MOP_wstrh:
656 return true;
657 default:
658 return false;
659 }
660 }
661
SplitMovImmOpndInstruction(int64 immVal,RegOperand & destReg,Insn * curInsn)662 void AArch64CGFunc::SplitMovImmOpndInstruction(int64 immVal, RegOperand &destReg, Insn *curInsn)
663 {
664 bool useMovz = BetterUseMOVZ(immVal);
665 bool useMovk = false;
666 /* get lower 32 bits of the immediate */
667 uint64 chunkLval = static_cast<uint64>(immVal) & 0xFFFFFFFFULL;
668 /* get upper 32 bits of the immediate */
669 uint64 chunkHval = (static_cast<uint64>(immVal) >> k32BitSize) & 0xFFFFFFFFULL;
670 int32 maxLoopTime = 4;
671
672 if (chunkLval == chunkHval) {
673 /* compute lower 32 bits, and then copy to higher 32 bits, so only 2 chunks need be processed */
674 maxLoopTime = 2;
675 }
676
677 uint64 sa = 0;
678 auto *bb = (curInsn != nullptr) ? curInsn->GetBB() : GetCurBB();
679 for (int64 i = 0; i < maxLoopTime; ++i, sa += k16BitSize) {
680 /* create an imm opereand which represents the i-th 16-bit chunk of the immediate */
681 uint64 chunkVal = (static_cast<uint64>(immVal) >> sa) & 0x0000FFFFULL;
682 if (useMovz ? (chunkVal == 0) : (chunkVal == 0x0000FFFFULL)) {
683 continue;
684 }
685 ImmOperand &src16 = CreateImmOperand(static_cast<int64>(chunkVal), k16BitSize, false);
686 BitShiftOperand *lslOpnd = GetLogicalShiftLeftOperand(sa, true);
687 Insn *newInsn = nullptr;
688 if (!useMovk) {
689 /* use movz or movn */
690 if (!useMovz) {
691 src16.BitwiseNegate();
692 }
693 MOperator mOpCode = useMovz ? MOP_xmovzri16 : MOP_xmovnri16;
694 newInsn = &GetInsnBuilder()->BuildInsn(mOpCode, destReg, src16, *lslOpnd);
695 useMovk = true;
696 } else {
697 newInsn = &GetInsnBuilder()->BuildInsn(MOP_xmovkri16, destReg, src16, *lslOpnd);
698 }
699 if (curInsn != nullptr) {
700 bb->InsertInsnBefore(*curInsn, *newInsn);
701 } else {
702 bb->AppendInsn(*newInsn);
703 }
704 }
705
706 if (maxLoopTime == 2) { // compute lower 32 bits, and copy to higher 32 bits, so only 2 chunks need be processed
707 /* copy lower 32 bits to higher 32 bits */
708 ImmOperand &immOpnd = CreateImmOperand(k32BitSize, k8BitSize, false);
709 Insn &insn = GetInsnBuilder()->BuildInsn(MOP_xbfirri6i6, destReg, destReg, immOpnd, immOpnd);
710 if (curInsn != nullptr) {
711 bb->InsertInsnBefore(*curInsn, insn);
712 } else {
713 bb->AppendInsn(insn);
714 }
715 }
716 }
717
SelectCopyRegOpnd(Operand & dest,PrimType dtype,Operand::OperandType opndType,uint32 dsize,Operand & src,PrimType stype)718 void AArch64CGFunc::SelectCopyRegOpnd(Operand &dest, PrimType dtype, Operand::OperandType opndType, uint32 dsize,
719 Operand &src, PrimType stype)
720 {
721 if (opndType != Operand::kOpdMem) {
722 if (!CGOptions::IsArm64ilp32()) {
723 DEBUG_ASSERT(stype != PTY_a32, "");
724 }
725 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(PickMovBetweenRegs(dtype, stype), dest, src));
726 return;
727 }
728 AArch64isa::MemoryOrdering memOrd = AArch64isa::kMoNone;
729 const MIRSymbol *sym = static_cast<MemOperand *>(&dest)->GetSymbol();
730 if ((sym != nullptr) && (sym->GetStorageClass() == kScGlobal) && sym->GetAttr(ATTR_memory_order_release)) {
731 memOrd = AArch64isa::kMoRelease;
732 }
733
734 if (memOrd != AArch64isa::kMoNone) {
735 AArch64CGFunc::SelectStoreRelease(dest, dtype, src, stype, memOrd, true);
736 return;
737 }
738
739 bool is64Bits = (dest.GetSize() == k64BitSize) ? true : false;
740 MOperator strMop = PickStInsn(dsize, stype);
741 if (!dest.IsMemoryAccessOperand()) {
742 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(strMop, src, dest));
743 return;
744 }
745
746 MemOperand *memOpnd = static_cast<MemOperand *>(&dest);
747 DEBUG_ASSERT(memOpnd != nullptr, "memOpnd should not be nullptr");
748 if (memOpnd->GetAddrMode() == MemOperand::kAddrModeLo12Li) {
749 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(strMop, src, dest));
750 return;
751 }
752 if (memOpnd->GetOffsetOperand() == nullptr) {
753 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(strMop, src, dest));
754 return;
755 }
756 ImmOperand *immOpnd = static_cast<ImmOperand *>(memOpnd->GetOffsetOperand());
757 DEBUG_ASSERT(immOpnd != nullptr, "immOpnd should not be nullptr");
758 int64 immVal = immOpnd->GetValue();
759 bool isIntactIndexed = memOpnd->IsIntactIndexed();
760 bool isPostIndexed = memOpnd->IsPostIndexed();
761 bool isPreIndexed = memOpnd->IsPreIndexed();
762 DEBUG_ASSERT(!isPostIndexed, "memOpnd should not be post-index type");
763 DEBUG_ASSERT(!isPreIndexed, "memOpnd should not be pre-index type");
764 bool isInRange = false;
765 if (!GetMirModule().IsCModule()) {
766 isInRange = IsImmediateValueInRange(strMop, immVal, is64Bits, isIntactIndexed, isPostIndexed, isPreIndexed);
767 } else {
768 isInRange = IsOperandImmValid(strMop, memOpnd, kInsnSecondOpnd);
769 }
770 bool isMopStr = IsStoreMop(strMop);
771 if (isInRange || !isMopStr) {
772 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(strMop, src, dest));
773 return;
774 }
775 DEBUG_ASSERT(memOpnd->GetBaseRegister() != nullptr, "nullptr check");
776 if (isIntactIndexed) {
777 memOpnd = &SplitOffsetWithAddInstruction(*memOpnd, dsize);
778 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(strMop, src, *memOpnd));
779 } else if (isPostIndexed || isPreIndexed) {
780 RegOperand ® = CreateRegisterOperandOfType(PTY_i64);
781 MOperator mopMov = MOP_xmovri64;
782 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopMov, reg, *immOpnd));
783 MOperator mopAdd = MOP_xaddrrr;
784 MemOperand &newDest =
785 GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, GetPrimTypeBitSize(dtype), memOpnd->GetBaseRegister(), nullptr,
786 &GetOrCreateOfstOpnd(0, k32BitSize), nullptr);
787 Insn &insn1 = GetInsnBuilder()->BuildInsn(strMop, src, newDest);
788 Insn &insn2 = GetInsnBuilder()->BuildInsn(mopAdd, *newDest.GetBaseRegister(), *newDest.GetBaseRegister(), reg);
789 if (isPostIndexed) {
790 GetCurBB()->AppendInsn(insn1);
791 GetCurBB()->AppendInsn(insn2);
792 } else {
793 /* isPreIndexed */
794 GetCurBB()->AppendInsn(insn2);
795 GetCurBB()->AppendInsn(insn1);
796 }
797 }
798 }
799
SelectCopy(Operand & dest,PrimType dtype,Operand & src,PrimType stype,BaseNode * baseNode)800 void AArch64CGFunc::SelectCopy(Operand &dest, PrimType dtype, Operand &src, PrimType stype, BaseNode *baseNode)
801 {
802 DEBUG_ASSERT(dest.IsRegister() || dest.IsMemoryAccessOperand(), "");
803 uint32 dsize = GetPrimTypeBitSize(dtype);
804 if (dest.IsRegister()) {
805 dsize = dest.GetSize();
806 }
807 Operand::OperandType opnd0Type = dest.GetKind();
808 Operand::OperandType opnd1Type = src.GetKind();
809 DEBUG_ASSERT(((dsize >= src.GetSize()) || (opnd0Type == Operand::kOpdRegister) || (opnd0Type == Operand::kOpdMem)),
810 "NYI");
811 DEBUG_ASSERT(((opnd0Type == Operand::kOpdRegister) || (src.GetKind() == Operand::kOpdRegister)),
812 "either src or dest should be register");
813
814 switch (opnd1Type) {
815 case Operand::kOpdMem:
816 SelectCopyMemOpnd(dest, dtype, dsize, src, stype);
817 break;
818 case Operand::kOpdOffset:
819 case Operand::kOpdImmediate:
820 SelectCopyImm(dest, dtype, static_cast<ImmOperand &>(src), stype);
821 break;
822 case Operand::kOpdFPImmediate:
823 CHECK_FATAL(static_cast<ImmOperand &>(src).GetValue() == 0, "NIY");
824 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn((dsize == k32BitSize) ? MOP_xvmovsr : MOP_xvmovdr, dest,
825 GetZeroOpnd(dsize)));
826 break;
827 case Operand::kOpdRegister: {
828 if (opnd0Type == Operand::kOpdRegister && IsPrimitiveVector(stype)) {
829 /* check vector reg to vector reg move */
830 CHECK_FATAL(IsPrimitiveVector(dtype), "invalid vectreg to vectreg move");
831 MOperator mop = (dsize <= k64BitSize) ? MOP_vmovuu : MOP_vmovvv;
832 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mop, AArch64CG::kMd[mop]);
833 vInsn.AddOpndChain(dest).AddOpndChain(src);
834 auto *vecSpecSrc = GetMemoryPool()->New<VectorRegSpec>(dsize >> k3ByteSize, k8BitSize);
835 auto *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(dsize >> k3ByteSize, k8BitSize);
836 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpecSrc);
837 GetCurBB()->AppendInsn(vInsn);
838 break;
839 }
840 if (dest.IsRegister()) {
841 RegOperand &desReg = static_cast<RegOperand &>(dest);
842 RegOperand &srcReg = static_cast<RegOperand &>(src);
843 if (desReg.GetRegisterNumber() == srcReg.GetRegisterNumber()) {
844 break;
845 }
846 }
847 SelectCopyRegOpnd(dest, dtype, opnd0Type, dsize, src, stype);
848 break;
849 }
850 default:
851 CHECK_FATAL(false, "NYI");
852 }
853 }
854
855 /* This function copies src to a register, the src can be an imm, mem or a label */
SelectCopy(Operand & src,PrimType stype,PrimType dtype)856 RegOperand &AArch64CGFunc::SelectCopy(Operand &src, PrimType stype, PrimType dtype)
857 {
858 RegOperand &dest = CreateRegisterOperandOfType(dtype);
859 SelectCopy(dest, dtype, src, stype);
860 return dest;
861 }
862
863 /*
864 * We need to adjust the offset of a stack allocated local variable
865 * if we store FP/SP before any other local variables to save an instruction.
866 * See AArch64CGFunc::OffsetAdjustmentForFPLR() in aarch64_cgfunc.cpp
867 *
868 * That is when we !UsedStpSubPairForCallFrameAllocation().
869 *
870 * Because we need to use the STP/SUB instruction pair to store FP/SP 'after'
871 * local variables when the call frame size is greater that the max offset
872 * value allowed for the STP instruction (we cannot use STP w/ prefix, LDP w/
873 * postfix), if UsedStpSubPairForCallFrameAllocation(), we don't need to
874 * adjust the offsets.
875 */
IsImmediateOffsetOutOfRange(const MemOperand & memOpnd,uint32 bitLen)876 bool AArch64CGFunc::IsImmediateOffsetOutOfRange(const MemOperand &memOpnd, uint32 bitLen)
877 {
878 DEBUG_ASSERT(bitLen >= k8BitSize, "bitlen error");
879 DEBUG_ASSERT(bitLen <= k128BitSize, "bitlen error");
880
881 if (bitLen >= k8BitSize) {
882 bitLen = static_cast<uint32>(RoundUp(bitLen, k8BitSize));
883 }
884 DEBUG_ASSERT((bitLen & (bitLen - 1)) == 0, "bitlen error");
885
886 MemOperand::AArch64AddressingMode mode = memOpnd.GetAddrMode();
887 if ((mode == MemOperand::kAddrModeBOi) && memOpnd.IsIntactIndexed()) {
888 OfstOperand *ofstOpnd = memOpnd.GetOffsetImmediate();
889 int32 offsetValue = ofstOpnd ? static_cast<int32>(ofstOpnd->GetOffsetValue()) : 0;
890 if (ofstOpnd && ofstOpnd->GetVary() == kUnAdjustVary) {
891 offsetValue +=
892 static_cast<int32>(static_cast<AArch64MemLayout *>(GetMemlayout())->RealStackFrameSize() + 0xff);
893 }
894 offsetValue += kAarch64IntregBytelen << 1; /* Refer to the above comment */
895 return MemOperand::IsPIMMOffsetOutOfRange(offsetValue, bitLen);
896 } else {
897 return false;
898 }
899 }
900
901 // This api is used to judge whether opnd is legal for mop.
902 // It is implemented by calling verify api of mop (InsnDesc -> Verify).
IsOperandImmValid(MOperator mOp,Operand * o,uint32 opndIdx) const903 bool AArch64CGFunc::IsOperandImmValid(MOperator mOp, Operand *o, uint32 opndIdx) const
904 {
905 const InsnDesc *md = &AArch64CG::kMd[mOp];
906 auto *opndProp = md->opndMD[opndIdx];
907 MemPool *localMp = memPoolCtrler.NewMemPool("opnd verify mempool", true);
908 auto *localAlloc = new MapleAllocator(localMp);
909 MapleVector<Operand *> testOpnds(md->opndMD.size(), localAlloc->Adapter());
910 testOpnds[opndIdx] = o;
911 bool flag = true;
912 Operand::OperandType opndTy = opndProp->GetOperandType();
913 if (opndTy == Operand::kOpdMem) {
914 auto *memOpnd = static_cast<MemOperand *>(o);
915 CHECK_FATAL(memOpnd != nullptr, "memOpnd should not be nullptr");
916 if (memOpnd->GetAddrMode() == MemOperand::kAddrModeBOrX &&
917 (!memOpnd->IsPostIndexed() && !memOpnd->IsPreIndexed())) {
918 delete localAlloc;
919 memPoolCtrler.DeleteMemPool(localMp);
920 return true;
921 }
922 OfstOperand *ofStOpnd = memOpnd->GetOffsetImmediate();
923 int64 offsetValue = ofStOpnd ? ofStOpnd->GetOffsetValue() : 0LL;
924 if (md->IsLoadStorePair() || (memOpnd->GetAddrMode() == MemOperand::kAddrModeBOi)) {
925 flag = md->Verify(testOpnds);
926 } else if (memOpnd->GetAddrMode() == MemOperand::kAddrModeLo12Li) {
927 if (offsetValue == 0) {
928 flag = md->Verify(testOpnds);
929 } else {
930 flag = false;
931 }
932 } else if (memOpnd->IsPostIndexed() || memOpnd->IsPreIndexed()) {
933 flag = (offsetValue <= static_cast<int64>(k256BitSizeInt) && offsetValue >= kNegative256BitSize);
934 }
935 } else if (opndTy == Operand::kOpdImmediate) {
936 flag = md->Verify(testOpnds);
937 }
938 delete localAlloc;
939 memPoolCtrler.DeleteMemPool(localMp);
940 return flag;
941 }
942
CreateReplacementMemOperand(uint32 bitLen,RegOperand & baseReg,int64 offset)943 MemOperand &AArch64CGFunc::CreateReplacementMemOperand(uint32 bitLen, RegOperand &baseReg, int64 offset)
944 {
945 return CreateMemOpnd(baseReg, offset, bitLen);
946 }
947
CheckIfSplitOffsetWithAdd(const MemOperand & memOpnd,uint32 bitLen) const948 bool AArch64CGFunc::CheckIfSplitOffsetWithAdd(const MemOperand &memOpnd, uint32 bitLen) const
949 {
950 if (memOpnd.GetAddrMode() != MemOperand::kAddrModeBOi || !memOpnd.IsIntactIndexed()) {
951 return false;
952 }
953 OfstOperand *ofstOpnd = memOpnd.GetOffsetImmediate();
954 int32 opndVal = static_cast<int32>(ofstOpnd->GetOffsetValue());
955 int32 maxPimm = memOpnd.GetMaxPIMM(bitLen);
956 int32 q0 = opndVal / maxPimm;
957 int32 addend = q0 * maxPimm;
958 int32 r0 = opndVal - addend;
959 int32 alignment = static_cast<int32_t>(memOpnd.GetImmediateOffsetAlignment(bitLen));
960 int32 r1 = static_cast<uint32>(r0) & ((1u << static_cast<uint32>(alignment)) - 1);
961 addend = addend + r1;
962 return (addend > 0);
963 }
964
GetBaseRegForSplit(uint32 baseRegNum)965 RegOperand *AArch64CGFunc::GetBaseRegForSplit(uint32 baseRegNum)
966 {
967 RegOperand *resOpnd = nullptr;
968 if (baseRegNum == AArch64reg::kRinvalid) {
969 resOpnd = &CreateRegisterOperandOfType(PTY_i64);
970 } else if (AArch64isa::IsPhysicalRegister(baseRegNum)) {
971 resOpnd = &GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(baseRegNum),
972 GetPointerSize() * kBitsPerByte, kRegTyInt);
973 } else {
974 resOpnd = &GetOrCreateVirtualRegisterOperand(baseRegNum);
975 }
976 return resOpnd;
977 }
978
979 /*
980 * When immediate of str/ldr is over 256bits, it should be aligned according to the reg byte size.
981 * Here we split the offset into (512 * n) and +/-(new Offset) when misaligned, to make sure that
982 * the new offet is always under 256 bits.
983 */
ConstraintOffsetToSafeRegion(uint32 bitLen,const MemOperand & memOpnd,const MIRSymbol * symbol)984 MemOperand &AArch64CGFunc::ConstraintOffsetToSafeRegion(uint32 bitLen, const MemOperand &memOpnd,
985 const MIRSymbol *symbol)
986 {
987 auto it = hashMemOpndTable.find(memOpnd);
988 if (it != hashMemOpndTable.end()) {
989 hashMemOpndTable.erase(memOpnd);
990 }
991 MemOperand::AArch64AddressingMode addrMode = memOpnd.GetAddrMode();
992 int32 offsetValue = static_cast<int32>(memOpnd.GetOffsetImmediate()->GetOffsetValue());
993 RegOperand *baseReg = memOpnd.GetBaseRegister();
994 RegOperand *resOpnd = GetBaseRegForSplit(kRinvalid);
995 MemOperand *newMemOpnd = nullptr;
996 if (addrMode == MemOperand::kAddrModeBOi) {
997 int32 val256 = k256BitSizeInt; /* const val is unsigned */
998 int32 val512 = k512BitSizeInt;
999 int32 multiplier = (offsetValue / val512) + static_cast<int32>(offsetValue % val512 > val256);
1000 int32 addMount = multiplier * val512;
1001 int32 newOffset = offsetValue - addMount;
1002 ImmOperand &immAddMount = CreateImmOperand(addMount, k64BitSize, true);
1003 if (memOpnd.GetOffsetImmediate()->GetVary() == kUnAdjustVary) {
1004 immAddMount.SetVary(kUnAdjustVary);
1005 }
1006 SelectAdd(*resOpnd, *baseReg, immAddMount, PTY_i64);
1007 newMemOpnd = &CreateReplacementMemOperand(bitLen, *resOpnd, newOffset);
1008 } else if (addrMode == MemOperand::kAddrModeLo12Li) {
1009 CHECK_FATAL(symbol != nullptr, "must have symbol");
1010 StImmOperand &stImmOpnd = CreateStImmOperand(*symbol, offsetValue, 0);
1011 SelectAdd(*resOpnd, *baseReg, stImmOpnd, PTY_i64);
1012 newMemOpnd = &CreateReplacementMemOperand(bitLen, *resOpnd, 0);
1013 }
1014 CHECK_FATAL(newMemOpnd != nullptr, "create memOpnd failed");
1015 newMemOpnd->SetStackMem(memOpnd.IsStackMem());
1016 return *newMemOpnd;
1017 }
1018
SplitAndGetRemained(const MemOperand & memOpnd,uint32 bitLen,RegOperand * resOpnd,int64 ofstVal,bool isDest,Insn * insn,bool forPair)1019 ImmOperand &AArch64CGFunc::SplitAndGetRemained(const MemOperand &memOpnd, uint32 bitLen, RegOperand *resOpnd,
1020 int64 ofstVal, bool isDest, Insn *insn, bool forPair)
1021 {
1022 auto it = hashMemOpndTable.find(memOpnd);
1023 if (it != hashMemOpndTable.end()) {
1024 hashMemOpndTable.erase(memOpnd);
1025 }
1026 /*
1027 * opndVal == Q0 * 32760(16380) + R0
1028 * R0 == Q1 * 8(4) + R1
1029 * ADDEND == Q0 * 32760(16380) + R1
1030 * NEW_OFFSET = Q1 * 8(4)
1031 * we want to generate two instructions:
1032 * ADD TEMP_REG, X29, ADDEND
1033 * LDR/STR TEMP_REG, [ TEMP_REG, #NEW_OFFSET ]
1034 */
1035 int32 maxPimm = 0;
1036 if (!forPair) {
1037 maxPimm = MemOperand::GetMaxPIMM(bitLen);
1038 } else {
1039 maxPimm = MemOperand::GetMaxPairPIMM(bitLen);
1040 }
1041 DEBUG_ASSERT(maxPimm != 0, "get max pimm failed");
1042
1043 int64 q0 = ofstVal / maxPimm + (ofstVal < 0 ? -1 : 0);
1044 int64 addend = q0 * maxPimm;
1045 int64 r0 = ofstVal - addend;
1046 int64 alignment = MemOperand::GetImmediateOffsetAlignment(bitLen);
1047 auto q1 = static_cast<int64>(static_cast<uint64>(r0) >> static_cast<uint64>(alignment));
1048 auto r1 = static_cast<int64>(static_cast<uint64>(r0) & ((1u << static_cast<uint64>(alignment)) - 1));
1049 auto remained = static_cast<int64>(static_cast<uint64>(q1) << static_cast<uint64>(alignment));
1050 addend = addend + r1;
1051 if (addend > 0) {
1052 int64 suffixClear = 0xfff;
1053 if (forPair) {
1054 suffixClear = 0xff;
1055 }
1056 int64 remainedTmp = remained + (addend & suffixClear);
1057 if (!MemOperand::IsPIMMOffsetOutOfRange(static_cast<int32>(remainedTmp), bitLen) &&
1058 ((static_cast<uint64>(remainedTmp) & ((1u << static_cast<uint64>(alignment)) - 1)) == 0)) {
1059 remained = remainedTmp;
1060 addend = (addend & ~suffixClear);
1061 }
1062 }
1063 ImmOperand &immAddend = CreateImmOperand(addend, k64BitSize, true);
1064 if (memOpnd.GetOffsetImmediate()->GetVary() == kUnAdjustVary) {
1065 immAddend.SetVary(kUnAdjustVary);
1066 }
1067 return immAddend;
1068 }
1069
SplitOffsetWithAddInstruction(const MemOperand & memOpnd,uint32 bitLen,uint32 baseRegNum,bool isDest,Insn * insn,bool forPair)1070 MemOperand &AArch64CGFunc::SplitOffsetWithAddInstruction(const MemOperand &memOpnd, uint32 bitLen, uint32 baseRegNum,
1071 bool isDest, Insn *insn, bool forPair)
1072 {
1073 DEBUG_ASSERT((memOpnd.GetAddrMode() == MemOperand::kAddrModeBOi), "expect kAddrModeBOi memOpnd");
1074 DEBUG_ASSERT(memOpnd.IsIntactIndexed(), "expect intactIndexed memOpnd");
1075 OfstOperand *ofstOpnd = memOpnd.GetOffsetImmediate();
1076 int64 ofstVal = ofstOpnd->GetOffsetValue();
1077 RegOperand *resOpnd = GetBaseRegForSplit(baseRegNum);
1078 ImmOperand &immAddend = SplitAndGetRemained(memOpnd, bitLen, resOpnd, ofstVal, isDest, insn, forPair);
1079 int64 remained = (ofstVal - immAddend.GetValue());
1080 RegOperand *origBaseReg = memOpnd.GetBaseRegister();
1081 DEBUG_ASSERT(origBaseReg != nullptr, "nullptr check");
1082 if (insn == nullptr) {
1083 SelectAdd(*resOpnd, *origBaseReg, immAddend, PTY_i64);
1084 } else {
1085 SelectAddAfterInsn(*resOpnd, *origBaseReg, immAddend, PTY_i64, isDest, *insn);
1086 }
1087 MemOperand &newMemOpnd = CreateReplacementMemOperand(bitLen, *resOpnd, remained);
1088 newMemOpnd.SetStackMem(memOpnd.IsStackMem());
1089 return newMemOpnd;
1090 }
1091
SelectDassign(DassignNode & stmt,Operand & opnd0)1092 void AArch64CGFunc::SelectDassign(DassignNode &stmt, Operand &opnd0)
1093 {
1094 SelectDassign(stmt.GetStIdx(), stmt.GetFieldID(), stmt.GetRHS()->GetPrimType(), opnd0);
1095 }
1096
1097 /* Extract the address of memOpnd.
1098 * 1. memcpy need address from memOpnd
1099 * 2. Used for SelectDassign when do optimization for volatile store, because the stlr instruction only allow
1100 * store to the memory addrress with the register base offset 0.
1101 * STLR <Wt>, [<Xn|SP>{,#0}], 32-bit variant (size = 10)
1102 * STLR <Xt>, [<Xn|SP>{,#0}], 64-bit variant (size = 11)
1103 */
ExtractMemBaseAddr(const MemOperand & memOpnd)1104 RegOperand *AArch64CGFunc::ExtractMemBaseAddr(const MemOperand &memOpnd)
1105 {
1106 const MIRSymbol *sym = memOpnd.GetSymbol();
1107 MemOperand::AArch64AddressingMode mode = memOpnd.GetAddrMode();
1108 if (mode == MemOperand::kAddrModeLiteral) {
1109 return nullptr;
1110 }
1111 RegOperand *baseOpnd = memOpnd.GetBaseRegister();
1112 OfstOperand *offsetOpnd = memOpnd.GetOffsetImmediate();
1113 int64 offset = (offsetOpnd == nullptr ? 0 : offsetOpnd->GetOffsetValue());
1114 DEBUG_ASSERT(baseOpnd != nullptr, "nullptr check");
1115 RegOperand &resultOpnd =
1116 CreateRegisterOperandOfType(baseOpnd->GetRegisterType(), baseOpnd->GetSize() / kBitsPerByte);
1117 bool is64Bits = (baseOpnd->GetSize() == k64BitSize);
1118 if (mode == MemOperand::kAddrModeLo12Li) {
1119 StImmOperand &stImm = CreateStImmOperand(*sym, offset, 0);
1120 Insn &addInsn = GetInsnBuilder()->BuildInsn(MOP_xadrpl12, resultOpnd, *baseOpnd, stImm);
1121 addInsn.SetComment("new add insn");
1122 GetCurBB()->AppendInsn(addInsn);
1123 } else if (mode == MemOperand::kAddrModeBOi) {
1124 if ((offsetOpnd != nullptr) && (offsetOpnd->GetOffsetValue() != 0)) {
1125 MOperator mOp = is64Bits ? MOP_xaddrri12 : MOP_waddrri12;
1126 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resultOpnd, *baseOpnd, *offsetOpnd));
1127 } else {
1128 return baseOpnd;
1129 }
1130 } else {
1131 CHECK_FATAL(mode == MemOperand::kAddrModeBOrX, "unexpect addressing mode.");
1132 RegOperand *regOpnd = static_cast<const MemOperand *>(&memOpnd)->GetIndexRegister();
1133 MOperator mOp = is64Bits ? MOP_xaddrrr : MOP_waddrrr;
1134 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resultOpnd, *baseOpnd, *regOpnd));
1135 }
1136 return &resultOpnd;
1137 }
1138
1139 /*
1140 * NOTE: I divided SelectDassign so that we can create "virtual" assignments
1141 * when selecting other complex Maple IR instructions. For example, the atomic
1142 * exchange and other intrinsics will need to assign its results to local
1143 * variables. Such Maple IR instructions are pltform-specific (e.g.
1144 * atomic_exchange can be implemented as one single machine intruction on x86_64
1145 * and ARMv8.1, but ARMv8.0 needs an LL/SC loop), therefore they cannot (in
1146 * principle) be lowered at BELowerer or CGLowerer.
1147 */
SelectDassign(StIdx stIdx,FieldID fieldId,PrimType rhsPType,Operand & opnd0)1148 void AArch64CGFunc::SelectDassign(StIdx stIdx, FieldID fieldId, PrimType rhsPType, Operand &opnd0)
1149 {
1150 MIRSymbol *symbol = GetFunction().GetLocalOrGlobalSymbol(stIdx);
1151 int32 offset = 0;
1152 bool parmCopy = false;
1153 if (fieldId != 0) {
1154 MIRStructType *structType = static_cast<MIRStructType *>(symbol->GetType());
1155 DEBUG_ASSERT(structType != nullptr, "SelectDassign: non-zero fieldID for non-structure");
1156 offset = GetBecommon().GetFieldOffset(*structType, fieldId).first;
1157 parmCopy = IsParamStructCopy(*symbol);
1158 }
1159 uint32 regSize = GetPrimTypeBitSize(rhsPType);
1160 MIRType *type = symbol->GetType();
1161 Operand &stOpnd = LoadIntoRegister(opnd0, IsPrimitiveInteger(rhsPType) || IsPrimitiveVectorInteger(rhsPType),
1162 regSize, IsSignedInteger(type->GetPrimType()));
1163 MOperator mOp = MOP_undef;
1164 if ((type->GetKind() == kTypeStruct) || (type->GetKind() == kTypeUnion)) {
1165 MIRStructType *structType = static_cast<MIRStructType *>(type);
1166 type = structType->GetFieldType(fieldId);
1167 } else if (type->GetKind() == kTypeClass) {
1168 MIRClassType *classType = static_cast<MIRClassType *>(type);
1169 type = classType->GetFieldType(fieldId);
1170 }
1171
1172 uint32 dataSize = GetPrimTypeBitSize(type->GetPrimType());
1173 if (type->GetPrimType() == PTY_agg) {
1174 dataSize = GetPrimTypeBitSize(PTY_a64);
1175 }
1176 MemOperand *memOpnd = nullptr;
1177 if (parmCopy) {
1178 memOpnd = &LoadStructCopyBase(*symbol, offset, static_cast<int>(dataSize));
1179 } else {
1180 memOpnd = &GetOrCreateMemOpnd(*symbol, offset, dataSize);
1181 }
1182 if ((memOpnd->GetMemVaryType() == kNotVary) && IsImmediateOffsetOutOfRange(*memOpnd, dataSize)) {
1183 memOpnd = &SplitOffsetWithAddInstruction(*memOpnd, dataSize);
1184 }
1185
1186 /* In bpl mode, a func symbol's type is represented as a MIRFuncType instead of a MIRPtrType (pointing to
1187 * MIRFuncType), so we allow `kTypeFunction` to appear here */
1188 DEBUG_ASSERT(((type->GetKind() == kTypeScalar) || (type->GetKind() == kTypePointer) ||
1189 (type->GetKind() == kTypeFunction) || (type->GetKind() == kTypeStruct) ||
1190 (type->GetKind() == kTypeUnion) || (type->GetKind() == kTypeArray)),
1191 "NYI dassign type");
1192 PrimType ptyp = type->GetPrimType();
1193 if (ptyp == PTY_agg) {
1194 ptyp = PTY_a64;
1195 }
1196
1197 AArch64isa::MemoryOrdering memOrd = AArch64isa::kMoNone;
1198 if (isVolStore) {
1199 RegOperand *baseOpnd = ExtractMemBaseAddr(*memOpnd);
1200 if (baseOpnd != nullptr) {
1201 memOpnd = &CreateMemOpnd(*baseOpnd, 0, dataSize);
1202 memOrd = AArch64isa::kMoRelease;
1203 isVolStore = false;
1204 }
1205 }
1206
1207 memOpnd =
1208 memOpnd->IsOffsetMisaligned(dataSize) ? &ConstraintOffsetToSafeRegion(dataSize, *memOpnd, symbol) : memOpnd;
1209 if (symbol->GetAsmAttr() != UStrIdx(0) && symbol->GetStorageClass() != kScPstatic &&
1210 symbol->GetStorageClass() != kScFstatic) {
1211 std::string regDesp = GlobalTables::GetUStrTable().GetStringFromStrIdx(symbol->GetAsmAttr());
1212 RegOperand &specifiedOpnd = GetOrCreatePhysicalRegisterOperand(regDesp);
1213 SelectCopy(specifiedOpnd, type->GetPrimType(), opnd0, rhsPType);
1214 } else if (memOrd == AArch64isa::kMoNone) {
1215 mOp = PickStInsn(GetPrimTypeBitSize(ptyp), ptyp);
1216 Insn &insn = GetInsnBuilder()->BuildInsn(mOp, stOpnd, *memOpnd);
1217 if (GetCG()->GenerateVerboseCG()) {
1218 insn.SetComment(GenerateMemOpndVerbose(*memOpnd));
1219 }
1220 GetCurBB()->AppendInsn(insn);
1221 } else {
1222 AArch64CGFunc::SelectStoreRelease(*memOpnd, ptyp, stOpnd, ptyp, memOrd, true);
1223 }
1224 }
1225
SelectDassignoff(DassignoffNode & stmt,Operand & opnd0)1226 void AArch64CGFunc::SelectDassignoff(DassignoffNode &stmt, Operand &opnd0)
1227 {
1228 MIRSymbol *symbol = GetFunction().GetLocalOrGlobalSymbol(stmt.stIdx);
1229 int64 offset = stmt.offset;
1230 uint32 size = GetPrimTypeSize(stmt.GetPrimType()) * k8ByteSize;
1231 MOperator mOp = (size == k16BitSize)
1232 ? MOP_wstrh
1233 : ((size == k32BitSize) ? MOP_wstr : ((size == k64BitSize) ? MOP_xstr : MOP_undef));
1234 CHECK_FATAL(mOp != MOP_undef, "illegal size for dassignoff");
1235 CHECK_NULL_FATAL(symbol);
1236 MemOperand *memOpnd = &GetOrCreateMemOpnd(*symbol, offset, size);
1237 if ((memOpnd->GetMemVaryType() == kNotVary) &&
1238 (IsImmediateOffsetOutOfRange(*memOpnd, size) || (offset % k8BitSize != 0))) {
1239 memOpnd = &SplitOffsetWithAddInstruction(*memOpnd, size);
1240 }
1241 Operand &stOpnd = LoadIntoRegister(opnd0, true, size, false);
1242 memOpnd = memOpnd->IsOffsetMisaligned(size) ? &ConstraintOffsetToSafeRegion(size, *memOpnd, symbol) : memOpnd;
1243 Insn &insn = GetInsnBuilder()->BuildInsn(mOp, stOpnd, *memOpnd);
1244 GetCurBB()->AppendInsn(insn);
1245 }
1246
SelectAssertNull(UnaryStmtNode & stmt)1247 void AArch64CGFunc::SelectAssertNull(UnaryStmtNode &stmt)
1248 {
1249 Operand *opnd0 = HandleExpr(stmt, *stmt.Opnd(0));
1250 RegOperand &baseReg = LoadIntoRegister(*opnd0, PTY_a64);
1251 auto &zwr = GetZeroOpnd(k32BitSize);
1252 auto &mem = CreateMemOpnd(baseReg, 0, k32BitSize);
1253 Insn &loadRef = GetInsnBuilder()->BuildInsn(MOP_wldr, zwr, mem);
1254 loadRef.SetDoNotRemove(true);
1255 if (GetCG()->GenerateVerboseCG()) {
1256 loadRef.SetComment("null pointer check");
1257 }
1258 GetCurBB()->AppendInsn(loadRef);
1259 }
1260
SelectAbort()1261 void AArch64CGFunc::SelectAbort()
1262 {
1263 RegOperand &inOpnd = GetOrCreatePhysicalRegisterOperand(R16, k64BitSize, kRegTyInt);
1264 auto &mem = CreateMemOpnd(inOpnd, 0, k64BitSize);
1265 Insn &movXzr = GetInsnBuilder()->BuildInsn(MOP_xmovri64, inOpnd, CreateImmOperand(0, k64BitSize, false));
1266 Insn &loadRef = GetInsnBuilder()->BuildInsn(MOP_wldr, GetZeroOpnd(k64BitSize), mem);
1267 loadRef.SetDoNotRemove(true);
1268 movXzr.SetDoNotRemove(true);
1269 GetCurBB()->AppendInsn(movXzr);
1270 GetCurBB()->AppendInsn(loadRef);
1271 SetCurBBKind(BB::kBBReturn);
1272 GetExitBBsVec().emplace_back(GetCurBB());
1273 }
1274
GetRegPrefixFromPrimType(PrimType pType,uint32 size,const std::string & constraint)1275 static std::string GetRegPrefixFromPrimType(PrimType pType, uint32 size, const std::string &constraint)
1276 {
1277 std::string regPrefix = "";
1278 /* memory access check */
1279 if (constraint.find("m") != std::string::npos || constraint.find("Q") != std::string::npos) {
1280 regPrefix += "[";
1281 }
1282 if (IsPrimitiveVector(pType)) {
1283 regPrefix += "v";
1284 } else if (IsPrimitiveInteger(pType)) {
1285 if (size == k32BitSize) {
1286 regPrefix += "w";
1287 } else {
1288 regPrefix += "x";
1289 }
1290 } else {
1291 if (size == k32BitSize) {
1292 regPrefix += "s";
1293 } else {
1294 regPrefix += "d";
1295 }
1296 }
1297 return regPrefix;
1298 }
1299
SelectAsm(AsmNode & node)1300 void AArch64CGFunc::SelectAsm(AsmNode &node)
1301 {
1302 SetHasAsm();
1303 if (Globals::GetInstance()->GetOptimLevel() > CGOptions::kLevel0) {
1304 if (GetCG()->GetCGOptions().DoLinearScanRegisterAllocation()) {
1305 CHECK_FATAL(false, "NIY, lsra unsupported inline asm!");
1306 }
1307 }
1308 Operand *asmString = &CreateStringOperand(node.asmString);
1309 ListOperand *listInputOpnd = CreateListOpnd(*GetFuncScopeAllocator());
1310 ListOperand *listOutputOpnd = CreateListOpnd(*GetFuncScopeAllocator());
1311 ListOperand *listClobber = CreateListOpnd(*GetFuncScopeAllocator());
1312 ListConstraintOperand *listInConstraint = memPool->New<ListConstraintOperand>(*GetFuncScopeAllocator());
1313 ListConstraintOperand *listOutConstraint = memPool->New<ListConstraintOperand>(*GetFuncScopeAllocator());
1314 ListConstraintOperand *listInRegPrefix = memPool->New<ListConstraintOperand>(*GetFuncScopeAllocator());
1315 ListConstraintOperand *listOutRegPrefix = memPool->New<ListConstraintOperand>(*GetFuncScopeAllocator());
1316 std::list<std::pair<Operand *, PrimType>> rPlusOpnd;
1317 bool noReplacement = false;
1318 if (node.asmString.find('$') == std::string::npos) {
1319 /* no replacements */
1320 noReplacement = true;
1321 }
1322 /* input constraints should be processed before OP_asm instruction */
1323 for (size_t i = 0; i < node.numOpnds; ++i) {
1324 /* process input constraint */
1325 std::string str = GlobalTables::GetUStrTable().GetStringFromStrIdx(node.inputConstraints[i]);
1326 bool isOutputTempNode = false;
1327 if (str[0] == '+') {
1328 isOutputTempNode = true;
1329 }
1330 listInConstraint->stringList.push_back(static_cast<StringOperand *>(&CreateStringOperand(str)));
1331 /* process input operands */
1332 switch (node.Opnd(i)->op) {
1333 case OP_dread: {
1334 DreadNode &dread = static_cast<DreadNode &>(*node.Opnd(i));
1335 Operand *inOpnd = SelectDread(node, dread);
1336 PrimType pType = dread.GetPrimType();
1337 listInputOpnd->PushOpnd(static_cast<RegOperand &>(*inOpnd));
1338 listInRegPrefix->stringList.push_back(static_cast<StringOperand *>(
1339 &CreateStringOperand(GetRegPrefixFromPrimType(pType, inOpnd->GetSize(), str))));
1340 if (isOutputTempNode) {
1341 rPlusOpnd.emplace_back(std::make_pair(inOpnd, pType));
1342 }
1343 break;
1344 }
1345 case OP_addrof: {
1346 auto &addrofNode = static_cast<AddrofNode &>(*node.Opnd(i));
1347 Operand *inOpnd = SelectAddrof(addrofNode, node);
1348 listInputOpnd->PushOpnd(static_cast<RegOperand &>(*inOpnd));
1349 PrimType pType = addrofNode.GetPrimType();
1350 listInRegPrefix->stringList.push_back(static_cast<StringOperand *>(
1351 &CreateStringOperand(GetRegPrefixFromPrimType(pType, inOpnd->GetSize(), str))));
1352 if (isOutputTempNode) {
1353 rPlusOpnd.emplace_back(std::make_pair(inOpnd, pType));
1354 }
1355 break;
1356 }
1357 case OP_addrofoff: {
1358 auto &addrofoffNode = static_cast<AddrofoffNode &>(*node.Opnd(i));
1359 Operand *inOpnd = SelectAddrofoff(addrofoffNode, node);
1360 listInputOpnd->PushOpnd(static_cast<RegOperand &>(*inOpnd));
1361 PrimType pType = addrofoffNode.GetPrimType();
1362 listInRegPrefix->stringList.push_back(static_cast<StringOperand *>(
1363 &CreateStringOperand(GetRegPrefixFromPrimType(pType, inOpnd->GetSize(), str))));
1364 if (isOutputTempNode) {
1365 rPlusOpnd.emplace_back(std::make_pair(inOpnd, pType));
1366 }
1367 break;
1368 }
1369 case OP_ireadoff: {
1370 auto *ireadoff = static_cast<IreadoffNode *>(node.Opnd(i));
1371 Operand *inOpnd = SelectIreadoff(node, *ireadoff);
1372 listInputOpnd->PushOpnd(static_cast<RegOperand &>(*inOpnd));
1373 PrimType pType = ireadoff->GetPrimType();
1374 listInRegPrefix->stringList.push_back(static_cast<StringOperand *>(
1375 &CreateStringOperand(GetRegPrefixFromPrimType(pType, inOpnd->GetSize(), str))));
1376 if (isOutputTempNode) {
1377 rPlusOpnd.emplace_back(std::make_pair(inOpnd, pType));
1378 }
1379 break;
1380 }
1381 case OP_ireadfpoff: {
1382 auto *ireadfpoff = static_cast<IreadFPoffNode *>(node.Opnd(i));
1383 Operand *inOpnd = SelectIreadfpoff(node, *ireadfpoff);
1384 listInputOpnd->PushOpnd(static_cast<RegOperand &>(*inOpnd));
1385 PrimType pType = ireadfpoff->GetPrimType();
1386 listInRegPrefix->stringList.push_back(static_cast<StringOperand *>(
1387 &CreateStringOperand(GetRegPrefixFromPrimType(pType, inOpnd->GetSize(), str))));
1388 if (isOutputTempNode) {
1389 rPlusOpnd.emplace_back(std::make_pair(inOpnd, pType));
1390 }
1391 break;
1392 }
1393 case OP_iread: {
1394 auto *iread = static_cast<IreadNode *>(node.Opnd(i));
1395 Operand *inOpnd = SelectIread(node, *iread);
1396 listInputOpnd->PushOpnd(static_cast<RegOperand &>(*inOpnd));
1397 PrimType pType = iread->GetPrimType();
1398 listInRegPrefix->stringList.push_back(static_cast<StringOperand *>(
1399 &CreateStringOperand(GetRegPrefixFromPrimType(pType, inOpnd->GetSize(), str))));
1400 if (isOutputTempNode) {
1401 rPlusOpnd.emplace_back(std::make_pair(inOpnd, pType));
1402 }
1403 break;
1404 }
1405 case OP_add: {
1406 auto *addNode = static_cast<BinaryNode *>(node.Opnd(i));
1407 Operand *inOpnd = SelectAdd(*addNode, *HandleExpr(*addNode, *addNode->Opnd(0)),
1408 *HandleExpr(*addNode, *addNode->Opnd(1)), node);
1409 listInputOpnd->PushOpnd(static_cast<RegOperand &>(*inOpnd));
1410 PrimType pType = addNode->GetPrimType();
1411 listInRegPrefix->stringList.push_back(static_cast<StringOperand *>(
1412 &CreateStringOperand(GetRegPrefixFromPrimType(pType, inOpnd->GetSize(), str))));
1413 if (isOutputTempNode) {
1414 rPlusOpnd.emplace_back(std::make_pair(inOpnd, pType));
1415 }
1416 break;
1417 }
1418
1419 case OP_constval: {
1420 CHECK_FATAL(!isOutputTempNode, "Unexpect");
1421 auto &constNode = static_cast<ConstvalNode &>(*node.Opnd(i));
1422 CHECK_FATAL(constNode.GetConstVal()->GetKind() == kConstInt,
1423 "expect MIRIntConst does not support float yet");
1424 MIRIntConst *mirIntConst = safe_cast<MIRIntConst>(constNode.GetConstVal());
1425 CHECK_FATAL(mirIntConst != nullptr, "just checking");
1426 int64 scale = mirIntConst->GetExtValue();
1427 if (str.find("r") != std::string::npos) {
1428 bool isSigned = scale < 0;
1429 ImmOperand &immOpnd = CreateImmOperand(scale, k64BitSize, isSigned);
1430 /* set default type as a 64 bit reg */
1431 PrimType pty = isSigned ? PTY_i64 : PTY_u64;
1432 auto &tempReg = static_cast<Operand &>(CreateRegisterOperandOfType(pty));
1433 SelectCopy(tempReg, pty, immOpnd, isSigned ? PTY_i64 : PTY_u64);
1434 listInputOpnd->PushOpnd(static_cast<RegOperand &>(tempReg));
1435 listInRegPrefix->stringList.push_back(static_cast<StringOperand *>(
1436 &CreateStringOperand(GetRegPrefixFromPrimType(pty, tempReg.GetSize(), str))));
1437 } else {
1438 RegOperand &inOpnd = GetOrCreatePhysicalRegisterOperand(RZR, k64BitSize, kRegTyInt);
1439 listInputOpnd->PushOpnd(static_cast<RegOperand &>(inOpnd));
1440
1441 listInRegPrefix->stringList.push_back(
1442 static_cast<StringOperand *>(&CreateStringOperand("i" + std::to_string(scale))));
1443 }
1444 break;
1445 }
1446 case OP_regread: {
1447 auto ®readNode = static_cast<RegreadNode &>(*node.Opnd(i));
1448 PregIdx pregIdx = regreadNode.GetRegIdx();
1449 RegOperand &inOpnd = GetOrCreateVirtualRegisterOperand(GetVirtualRegNOFromPseudoRegIdx(pregIdx));
1450 listInputOpnd->PushOpnd(static_cast<RegOperand &>(inOpnd));
1451 MIRPreg *preg = GetFunction().GetPregTab()->PregFromPregIdx(pregIdx);
1452 PrimType pType = preg->GetPrimType();
1453 listInRegPrefix->stringList.push_back(static_cast<StringOperand *>(
1454 &CreateStringOperand(GetRegPrefixFromPrimType(pType, inOpnd.GetSize(), str))));
1455 if (isOutputTempNode) {
1456 rPlusOpnd.emplace_back(std::make_pair(&static_cast<Operand &>(inOpnd), pType));
1457 }
1458 break;
1459 }
1460 default:
1461 CHECK_FATAL(0, "Inline asm input expression not handled");
1462 }
1463 }
1464 std::vector<Operand *> intrnOpnds;
1465 intrnOpnds.emplace_back(asmString);
1466 intrnOpnds.emplace_back(listOutputOpnd);
1467 intrnOpnds.emplace_back(listClobber);
1468 intrnOpnds.emplace_back(listInputOpnd);
1469 intrnOpnds.emplace_back(listOutConstraint);
1470 intrnOpnds.emplace_back(listInConstraint);
1471 intrnOpnds.emplace_back(listOutRegPrefix);
1472 intrnOpnds.emplace_back(listInRegPrefix);
1473 Insn *asmInsn = &GetInsnBuilder()->BuildInsn(MOP_asm, intrnOpnds);
1474 GetCurBB()->AppendInsn(*asmInsn);
1475
1476 /* process listOutputOpnd */
1477 for (size_t i = 0; i < node.asmOutputs.size(); ++i) {
1478 bool isOutputTempNode = false;
1479 RegOperand *rPOpnd = nullptr;
1480 /* process output constraint */
1481 std::string str = GlobalTables::GetUStrTable().GetStringFromStrIdx(node.outputConstraints[i]);
1482
1483 listOutConstraint->stringList.push_back(static_cast<StringOperand *>(&CreateStringOperand(str)));
1484 if (str[0] == '+') {
1485 CHECK_FATAL(!rPlusOpnd.empty(), "Need r+ operand");
1486 rPOpnd = static_cast<RegOperand *>((rPlusOpnd.begin()->first));
1487 listOutputOpnd->PushOpnd(*rPOpnd);
1488 listOutRegPrefix->stringList.push_back(static_cast<StringOperand *>(
1489 &CreateStringOperand(GetRegPrefixFromPrimType(rPlusOpnd.begin()->second, rPOpnd->GetSize(), str))));
1490 if (!rPlusOpnd.empty()) {
1491 rPlusOpnd.pop_front();
1492 }
1493 isOutputTempNode = true;
1494 }
1495 if (str.find("Q") != std::string::npos || str.find("m") != std::string::npos) {
1496 continue;
1497 }
1498 /* process output operands */
1499 StIdx stIdx = node.asmOutputs[i].first;
1500 RegFieldPair regFieldPair = node.asmOutputs[i].second;
1501 if (regFieldPair.IsReg()) {
1502 PregIdx pregIdx = static_cast<PregIdx>(regFieldPair.GetPregIdx());
1503 CHECK_NULL_FATAL(mirModule.CurFunction());
1504 MIRPreg *mirPreg = mirModule.CurFunction()->GetPregTab()->PregFromPregIdx(pregIdx);
1505 RegOperand *outOpnd = isOutputTempNode
1506 ? rPOpnd
1507 : &GetOrCreateVirtualRegisterOperand(GetVirtualRegNOFromPseudoRegIdx(pregIdx));
1508 PrimType srcType = mirPreg->GetPrimType();
1509 PrimType destType = srcType;
1510 if (GetPrimTypeBitSize(destType) < k32BitSize) {
1511 destType = IsSignedInteger(destType) ? PTY_i32 : PTY_u32;
1512 }
1513 RegType rtype = GetRegTyFromPrimTy(srcType);
1514 RegOperand &opnd0 = isOutputTempNode
1515 ? GetOrCreateVirtualRegisterOperand(GetVirtualRegNOFromPseudoRegIdx(pregIdx))
1516 : CreateVirtualRegisterOperand(NewVReg(rtype, GetPrimTypeSize(srcType)));
1517 SelectCopy(opnd0, destType, *outOpnd, srcType);
1518 if (!isOutputTempNode) {
1519 listOutputOpnd->PushOpnd(static_cast<RegOperand &>(*outOpnd));
1520 listOutRegPrefix->stringList.push_back(static_cast<StringOperand *>(
1521 &CreateStringOperand(GetRegPrefixFromPrimType(srcType, outOpnd->GetSize(), str))));
1522 }
1523 } else {
1524 MIRSymbol *var;
1525 if (stIdx.IsGlobal()) {
1526 var = GlobalTables::GetGsymTable().GetSymbolFromStidx(stIdx.Idx());
1527 } else {
1528 CHECK_NULL_FATAL(mirModule.CurFunction());
1529 var = mirModule.CurFunction()->GetSymbolTabItem(stIdx.Idx());
1530 }
1531 CHECK_FATAL(var != nullptr, "var should not be nullptr");
1532 if (!noReplacement || var->GetAsmAttr() != UStrIdx(0)) {
1533 RegOperand *outOpnd = nullptr;
1534 PrimType pty = GlobalTables::GetTypeTable().GetTypeTable().at(var->GetTyIdx())->GetPrimType();
1535 if (var->GetAsmAttr() != UStrIdx(0)) {
1536 std::string regDesp = GlobalTables::GetUStrTable().GetStringFromStrIdx(var->GetAsmAttr());
1537 outOpnd = &GetOrCreatePhysicalRegisterOperand(regDesp);
1538 } else {
1539 RegType rtype = GetRegTyFromPrimTy(pty);
1540 outOpnd =
1541 isOutputTempNode ? rPOpnd : &CreateVirtualRegisterOperand(NewVReg(rtype, GetPrimTypeSize(pty)));
1542 }
1543 SaveReturnValueInLocal(node.asmOutputs, i, PTY_a64, *outOpnd, node);
1544 if (!isOutputTempNode) {
1545 listOutputOpnd->PushOpnd(static_cast<RegOperand &>(*outOpnd));
1546 listOutRegPrefix->stringList.push_back(static_cast<StringOperand *>(
1547 &CreateStringOperand(GetRegPrefixFromPrimType(pty, outOpnd->GetSize(), str))));
1548 }
1549 }
1550 }
1551 }
1552 if (noReplacement) {
1553 return;
1554 }
1555
1556 /* process listClobber */
1557 for (size_t i = 0; i < node.clobberList.size(); ++i) {
1558 std::string str = GlobalTables::GetUStrTable().GetStringFromStrIdx(node.clobberList[i]);
1559 auto regno = static_cast<regno_t>(str[1] - '0');
1560 if (str[2] >= '0' && str[2] <= '9') { // if third char (index 2) is num, add to regno
1561 regno = regno * kDecimalMax + static_cast<uint32>((str[2] - '0'));
1562 }
1563 RegOperand *reg;
1564 switch (str[0]) {
1565 case 'w': {
1566 reg = &GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(regno + R0), k32BitSize, kRegTyInt);
1567 listClobber->PushOpnd(*reg);
1568 break;
1569 }
1570 case 'x': {
1571 reg = &GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(regno + R0), k64BitSize, kRegTyInt);
1572 listClobber->PushOpnd(*reg);
1573 break;
1574 }
1575 case 's': {
1576 reg = &GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(regno + V0), k32BitSize, kRegTyFloat);
1577 listClobber->PushOpnd(*reg);
1578 break;
1579 }
1580 case 'd': {
1581 reg = &GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(regno + V0), k64BitSize, kRegTyFloat);
1582 listClobber->PushOpnd(*reg);
1583 break;
1584 }
1585 case 'v': {
1586 reg = &GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(regno + V0), k64BitSize, kRegTyFloat);
1587 listClobber->PushOpnd(*reg);
1588 break;
1589 }
1590 case 'c': {
1591 asmInsn->SetAsmDefCondCode();
1592 break;
1593 }
1594 case 'm': {
1595 asmInsn->SetAsmModMem();
1596 break;
1597 }
1598 default:
1599 CHECK_FATAL(0, "Inline asm clobber list not handled");
1600 }
1601 }
1602 }
1603
SelectRegassign(RegassignNode & stmt,Operand & opnd0)1604 void AArch64CGFunc::SelectRegassign(RegassignNode &stmt, Operand &opnd0)
1605 {
1606 if (GetCG()->IsLmbc()) {
1607 PrimType lhsSize = stmt.GetPrimType();
1608 PrimType rhsSize = stmt.Opnd(0)->GetPrimType();
1609 if (lhsSize != rhsSize && stmt.Opnd(0)->GetOpCode() == OP_ireadoff) {
1610 Insn *prev = GetCurBB()->GetLastMachineInsn();
1611 if (prev && (prev->GetMachineOpcode() == MOP_wldrsb || prev->GetMachineOpcode() == MOP_wldrsh)) {
1612 opnd0.SetSize(GetPrimTypeBitSize(stmt.GetPrimType()));
1613 prev->SetMOP(AArch64CG::kMd[prev->GetMachineOpcode() == MOP_wldrsb ? MOP_xldrsb : MOP_xldrsh]);
1614 } else if (prev && (prev->GetMachineOpcode() == MOP_wldr && stmt.GetPrimType() == PTY_i64)) {
1615 opnd0.SetSize(GetPrimTypeBitSize(stmt.GetPrimType()));
1616 prev->SetMOP(AArch64CG::kMd[MOP_xldrsw]);
1617 }
1618 }
1619 }
1620 RegOperand *regOpnd = nullptr;
1621 PregIdx pregIdx = stmt.GetRegIdx();
1622 if (IsSpecialPseudoRegister(pregIdx)) {
1623 if (GetCG()->IsLmbc() && stmt.GetPrimType() == PTY_agg) {
1624 if (static_cast<RegOperand &>(opnd0).IsOfIntClass()) {
1625 regOpnd = &GetOrCreateSpecialRegisterOperand(-pregIdx, PTY_i64);
1626 } else if (opnd0.GetSize() <= k4ByteSize) {
1627 regOpnd = &GetOrCreateSpecialRegisterOperand(-pregIdx, PTY_f32);
1628 } else {
1629 regOpnd = &GetOrCreateSpecialRegisterOperand(-pregIdx, PTY_f64);
1630 }
1631 } else {
1632 regOpnd = &GetOrCreateSpecialRegisterOperand(-pregIdx, stmt.GetPrimType());
1633 }
1634 } else {
1635 regOpnd = &GetOrCreateVirtualRegisterOperand(GetVirtualRegNOFromPseudoRegIdx(pregIdx));
1636 }
1637 /* look at rhs */
1638 PrimType rhsType = stmt.Opnd(0)->GetPrimType();
1639 if (GetCG()->IsLmbc() && rhsType == PTY_agg) {
1640 /* This occurs when a call returns a small struct */
1641 /* The subtree should already taken care of the agg type that is in excess of 8 bytes */
1642 rhsType = PTY_i64;
1643 }
1644 DEBUG_ASSERT(regOpnd != nullptr, "null ptr check!");
1645 Operand *srcOpnd = &opnd0;
1646 if (GetPrimTypeSize(stmt.GetPrimType()) > GetPrimTypeSize(rhsType) && IsPrimitiveInteger(rhsType)) {
1647 CHECK_FATAL(IsPrimitiveInteger(stmt.GetPrimType()), "NIY");
1648 srcOpnd = &CreateRegisterOperandOfType(stmt.GetPrimType());
1649 SelectCvtInt2Int(nullptr, srcOpnd, &opnd0, rhsType, stmt.GetPrimType());
1650 }
1651 SelectCopy(*regOpnd, stmt.GetPrimType(), *srcOpnd, rhsType, stmt.GetRHS());
1652
1653 if (GetCG()->GenerateVerboseCG()) {
1654 if (GetCurBB()->GetLastInsn()) {
1655 GetCurBB()->GetLastInsn()->AppendComment(" regassign %" + std::to_string(pregIdx) + "; ");
1656 } else if (GetCurBB()->GetPrev()->GetLastInsn()) {
1657 GetCurBB()->GetPrev()->GetLastInsn()->AppendComment(" regassign %" + std::to_string(pregIdx) + "; ");
1658 }
1659 }
1660
1661 if ((Globals::GetInstance()->GetOptimLevel() == CGOptions::kLevel0) && (pregIdx >= 0)) {
1662 MemOperand *dest = GetPseudoRegisterSpillMemoryOperand(pregIdx);
1663 PrimType stype = GetTypeFromPseudoRegIdx(pregIdx);
1664 MIRPreg *preg = GetFunction().GetPregTab()->PregFromPregIdx(pregIdx);
1665 uint32 srcBitLength = GetPrimTypeSize(preg->GetPrimType()) * kBitsPerByte;
1666 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(PickStInsn(srcBitLength, stype), *regOpnd, *dest));
1667 } else if (regOpnd->GetRegisterNumber() == R0 || regOpnd->GetRegisterNumber() == R1) {
1668 Insn &pseudo = GetInsnBuilder()->BuildInsn(MOP_pseudo_ret_int, *regOpnd);
1669 GetCurBB()->AppendInsn(pseudo);
1670 } else if (regOpnd->GetRegisterNumber() >= V0 && regOpnd->GetRegisterNumber() <= V3) {
1671 Insn &pseudo = GetInsnBuilder()->BuildInsn(MOP_pseudo_ret_float, *regOpnd);
1672 GetCurBB()->AppendInsn(pseudo);
1673 }
1674 if (stmt.GetPrimType() == PTY_ref) {
1675 regOpnd->SetIsReference(true);
1676 AddReferenceReg(regOpnd->GetRegisterNumber());
1677 }
1678 if (pregIdx > 0) {
1679 // special MIRPreg is not supported
1680 SetPregIdx2Opnd(pregIdx, *regOpnd);
1681 }
1682 const auto &derived2BaseRef = GetFunction().GetDerived2BaseRef();
1683 auto itr = derived2BaseRef.find(pregIdx);
1684 if (itr != derived2BaseRef.end()) {
1685 auto *opnd = GetOpndFromPregIdx(itr->first);
1686 CHECK_FATAL(opnd != nullptr, "pregIdx has not been assigned Operand");
1687 auto &derivedRegOpnd = static_cast<RegOperand &>(*opnd);
1688 opnd = GetOpndFromPregIdx(itr->second);
1689 CHECK_FATAL(opnd != nullptr, "pregIdx has not been assigned Operand");
1690 auto &baseRegOpnd = static_cast<RegOperand &>(*opnd);
1691 derivedRegOpnd.SetBaseRefOpnd(baseRegOpnd);
1692 }
1693 }
1694
GenFormalMemOpndWithSymbol(const MIRSymbol & sym,int64 offset)1695 MemOperand *AArch64CGFunc::GenFormalMemOpndWithSymbol(const MIRSymbol &sym, int64 offset)
1696 {
1697 MemOperand *memOpnd = nullptr;
1698 if (IsParamStructCopy(sym)) {
1699 memOpnd = &GetOrCreateMemOpnd(sym, 0, k64BitSize);
1700 RegOperand *vreg = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
1701 Insn &ldInsn = GetInsnBuilder()->BuildInsn(PickLdInsn(k64BitSize, PTY_i64), *vreg, *memOpnd);
1702 GetCurBB()->AppendInsn(ldInsn);
1703 return CreateMemOperand(k64BitSize, *vreg, CreateImmOperand(offset, k32BitSize, false), sym.IsVolatile());
1704 }
1705 return &GetOrCreateMemOpnd(sym, offset, k64BitSize);
1706 }
1707
FixLargeMemOpnd(MemOperand & memOpnd,uint32 align)1708 MemOperand *AArch64CGFunc::FixLargeMemOpnd(MemOperand &memOpnd, uint32 align)
1709 {
1710 MemOperand *lhsMemOpnd = &memOpnd;
1711 if ((lhsMemOpnd->GetMemVaryType() == kNotVary) && IsImmediateOffsetOutOfRange(*lhsMemOpnd, align * kBitsPerByte)) {
1712 RegOperand *addReg = &CreateRegisterOperandOfType(PTY_i64);
1713 lhsMemOpnd = &SplitOffsetWithAddInstruction(*lhsMemOpnd, align * k8BitSize, addReg->GetRegisterNumber());
1714 }
1715 return lhsMemOpnd;
1716 }
1717
FixLargeMemOpnd(MOperator mOp,MemOperand & memOpnd,uint32 dSize,uint32 opndIdx)1718 MemOperand *AArch64CGFunc::FixLargeMemOpnd(MOperator mOp, MemOperand &memOpnd, uint32 dSize, uint32 opndIdx)
1719 {
1720 auto *a64MemOpnd = &memOpnd;
1721 if ((a64MemOpnd->GetMemVaryType() == kNotVary) && !IsOperandImmValid(mOp, &memOpnd, opndIdx)) {
1722 if (opndIdx == kInsnSecondOpnd) {
1723 a64MemOpnd = &SplitOffsetWithAddInstruction(*a64MemOpnd, dSize);
1724 } else if (opndIdx == kInsnThirdOpnd) {
1725 a64MemOpnd =
1726 &SplitOffsetWithAddInstruction(*a64MemOpnd, dSize, AArch64reg::kRinvalid, false, nullptr, true);
1727 } else {
1728 CHECK_FATAL(false, "NYI");
1729 }
1730 }
1731 return a64MemOpnd;
1732 }
1733
GenLargeAggFormalMemOpnd(const MIRSymbol & sym,uint32 align,int64 offset,bool needLow12)1734 MemOperand *AArch64CGFunc::GenLargeAggFormalMemOpnd(const MIRSymbol &sym, uint32 align, int64 offset, bool needLow12)
1735 {
1736 MemOperand *memOpnd;
1737 if (sym.GetStorageClass() == kScFormal && GetBecommon().GetTypeSize(sym.GetTyIdx()) > k16ByteSize) {
1738 /* formal of size of greater than 16 is copied by the caller and the pointer to it is passed. */
1739 /* otherwise it is passed in register and is accessed directly. */
1740 memOpnd = &GetOrCreateMemOpnd(sym, 0, align * kBitsPerByte);
1741 RegOperand *vreg = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
1742 Insn &ldInsn = GetInsnBuilder()->BuildInsn(PickLdInsn(k64BitSize, PTY_i64), *vreg, *memOpnd);
1743 GetCurBB()->AppendInsn(ldInsn);
1744 memOpnd = &GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, k64BitSize, vreg, nullptr,
1745 &GetOrCreateOfstOpnd(static_cast<uint64>(offset), k32BitSize), nullptr);
1746 } else {
1747 memOpnd = &GetOrCreateMemOpnd(sym, offset, align * kBitsPerByte, false, needLow12);
1748 }
1749 return FixLargeMemOpnd(*memOpnd, align);
1750 }
1751
PrepareMemcpyParamOpnd(bool isLo12,const MIRSymbol & symbol,int64 offsetVal,RegOperand & BaseReg)1752 RegOperand *AArch64CGFunc::PrepareMemcpyParamOpnd(bool isLo12, const MIRSymbol &symbol, int64 offsetVal,
1753 RegOperand &BaseReg)
1754 {
1755 RegOperand *tgtAddr = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
1756 if (isLo12) {
1757 StImmOperand &stImm = CreateStImmOperand(symbol, 0, 0);
1758 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xadrpl12, *tgtAddr, BaseReg, stImm));
1759 } else {
1760 ImmOperand &imm = CreateImmOperand(offsetVal, k64BitSize, false);
1761 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xaddrri12, *tgtAddr, BaseReg, imm));
1762 }
1763 return tgtAddr;
1764 }
1765
PrepareMemcpyParamOpnd(int64 offset,Operand & exprOpnd)1766 RegOperand *AArch64CGFunc::PrepareMemcpyParamOpnd(int64 offset, Operand &exprOpnd)
1767 {
1768 RegOperand *tgtAddr = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
1769 OfstOperand *ofstOpnd = &GetOrCreateOfstOpnd(static_cast<uint64>(offset), k32BitSize);
1770 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xaddrri12, *tgtAddr, exprOpnd, *ofstOpnd));
1771 return tgtAddr;
1772 }
1773
PrepareMemcpyParamOpnd(uint64 copySize)1774 RegOperand *AArch64CGFunc::PrepareMemcpyParamOpnd(uint64 copySize)
1775 {
1776 RegOperand *vregMemcpySize = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
1777 ImmOperand *sizeOpnd = &CreateImmOperand(static_cast<int64>(copySize), k64BitSize, false);
1778 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wmovri32, *vregMemcpySize, *sizeOpnd));
1779 return vregMemcpySize;
1780 }
1781
AggtStrLdrInsert(bool bothUnion,Insn * lastStrLdr,Insn & newStrLdr)1782 Insn *AArch64CGFunc::AggtStrLdrInsert(bool bothUnion, Insn *lastStrLdr, Insn &newStrLdr)
1783 {
1784 if (bothUnion) {
1785 if (lastStrLdr == nullptr) {
1786 GetCurBB()->AppendInsn(newStrLdr);
1787 } else {
1788 GetCurBB()->InsertInsnAfter(*lastStrLdr, newStrLdr);
1789 }
1790 } else {
1791 GetCurBB()->AppendInsn(newStrLdr);
1792 }
1793 return &newStrLdr;
1794 }
1795
SelectRhsMemOpnd(BaseNode & rhsStmt,bool & isRefField)1796 MemOperand *AArch64CGFunc::SelectRhsMemOpnd(BaseNode &rhsStmt, bool &isRefField)
1797 {
1798 MemOperand *rhsMemOpnd = nullptr;
1799 if (rhsStmt.GetOpCode() == OP_dread) {
1800 MemRWNodeHelper rhsMemHelper(rhsStmt, GetFunction(), GetBecommon());
1801 auto *rhsSymbol = rhsMemHelper.GetSymbol();
1802 rhsMemOpnd = GenFormalMemOpndWithSymbol(*rhsSymbol, rhsMemHelper.GetByteOffset());
1803 isRefField = rhsMemHelper.IsRefField();
1804 } else {
1805 DEBUG_ASSERT(rhsStmt.GetOpCode() == OP_iread, "SelectRhsMemOpnd: NYI");
1806 auto &rhsIread = static_cast<IreadNode &>(rhsStmt);
1807 MemRWNodeHelper rhsMemHelper(rhsIread, GetFunction(), GetBecommon());
1808 auto *rhsAddrOpnd = HandleExpr(rhsIread, *rhsIread.Opnd(0));
1809 auto &rhsAddr = LoadIntoRegister(*rhsAddrOpnd, rhsIread.Opnd(0)->GetPrimType());
1810 auto &rhsOfstOpnd = CreateImmOperand(rhsMemHelper.GetByteOffset(), k32BitSize, false);
1811 rhsMemOpnd = CreateMemOperand(k64BitSize, rhsAddr, rhsOfstOpnd, rhsIread.IsVolatile());
1812 isRefField = rhsMemHelper.IsRefField();
1813 }
1814 return rhsMemOpnd;
1815 }
1816
SelectRhsMemOpnd(BaseNode & rhsStmt)1817 MemOperand *AArch64CGFunc::SelectRhsMemOpnd(BaseNode &rhsStmt)
1818 {
1819 bool isRefField = false;
1820 return SelectRhsMemOpnd(rhsStmt, isRefField);
1821 }
1822
GetOrCreateLocator(CallConvKind cc)1823 CCImpl *AArch64CGFunc::GetOrCreateLocator(CallConvKind cc)
1824 {
1825 auto it = hashCCTable.find(cc);
1826 if (it != hashCCTable.end()) {
1827 it->second->Init();
1828 return it->second;
1829 }
1830 CCImpl *res = nullptr;
1831 if (cc == kCCall) {
1832 res = memPool->New<AArch64CallConvImpl>(GetBecommon());
1833 } else if (cc == kWebKitJS) {
1834 res = memPool->New<AArch64WebKitJSCC>(GetBecommon());
1835 } else if (cc == kGHC) {
1836 res = memPool->New<GHCCC>(GetBecommon());
1837 } else {
1838 CHECK_FATAL(false, "unsupported yet");
1839 }
1840 hashCCTable[cc] = res;
1841 return res;
1842 }
SelectAggDassign(DassignNode & stmt)1843 void AArch64CGFunc::SelectAggDassign(DassignNode &stmt)
1844 {
1845 DEBUG_ASSERT(stmt.Opnd(0) != nullptr, "null ptr check");
1846 MemRWNodeHelper lhsMemHelper(stmt, GetFunction(), GetBecommon());
1847 auto *lhsSymbol = lhsMemHelper.GetSymbol();
1848 DEBUG_ASSERT(lhsSymbol != nullptr, "nullptr check");
1849 auto *lhsMemOpnd = GenFormalMemOpndWithSymbol(*lhsSymbol, lhsMemHelper.GetByteOffset());
1850
1851 bool isRefField = false;
1852 auto *rhsMemOpnd = SelectRhsMemOpnd(*stmt.GetRHS(), isRefField);
1853 SelectMemCopy(*lhsMemOpnd, *rhsMemOpnd, lhsMemHelper.GetMemSize(), isRefField, &stmt, stmt.GetRHS());
1854 }
1855
GetPointedToType(const MIRPtrType & pointerType)1856 static MIRType *GetPointedToType(const MIRPtrType &pointerType)
1857 {
1858 MIRType *aType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(pointerType.GetPointedTyIdx());
1859 if (aType->GetKind() == kTypeArray) {
1860 MIRArrayType *arrayType = static_cast<MIRArrayType *>(aType);
1861 return GlobalTables::GetTypeTable().GetTypeFromTyIdx(arrayType->GetElemTyIdx());
1862 }
1863 if (aType->GetKind() == kTypeFArray || aType->GetKind() == kTypeJArray) {
1864 MIRFarrayType *farrayType = static_cast<MIRFarrayType *>(aType);
1865 return GlobalTables::GetTypeTable().GetTypeFromTyIdx(farrayType->GetElemTyIdx());
1866 }
1867 return aType;
1868 }
1869
SelectIassign(IassignNode & stmt)1870 void AArch64CGFunc::SelectIassign(IassignNode &stmt)
1871 {
1872 int32 offset = 0;
1873 MIRPtrType *pointerType = static_cast<MIRPtrType *>(GlobalTables::GetTypeTable().GetTypeFromTyIdx(stmt.GetTyIdx()));
1874 DEBUG_ASSERT(pointerType != nullptr, "expect a pointer type at iassign node");
1875 MIRType *pointedType = nullptr;
1876 bool isRefField = false;
1877 AArch64isa::MemoryOrdering memOrd = AArch64isa::kMoNone;
1878
1879 if (stmt.GetFieldID() != 0) {
1880 MIRType *pointedTy = GlobalTables::GetTypeTable().GetTypeFromTyIdx(pointerType->GetPointedTyIdx());
1881 MIRStructType *structType = nullptr;
1882 if (pointedTy->GetKind() != kTypeJArray) {
1883 structType = static_cast<MIRStructType *>(pointedTy);
1884 } else {
1885 structType = static_cast<MIRJarrayType *>(pointedTy)->GetParentType();
1886 }
1887 DEBUG_ASSERT(structType != nullptr, "SelectIassign: non-zero fieldID for non-structure");
1888 pointedType = structType->GetFieldType(stmt.GetFieldID());
1889 offset = GetBecommon().GetFieldOffset(*structType, stmt.GetFieldID()).first;
1890 isRefField = GetBecommon().IsRefField(*structType, stmt.GetFieldID());
1891 } else {
1892 pointedType = GetPointedToType(*pointerType);
1893 }
1894
1895 PrimType styp = stmt.GetRHS()->GetPrimType();
1896 Operand *valOpnd = HandleExpr(stmt, *stmt.GetRHS());
1897 Operand &srcOpnd = LoadIntoRegister(*valOpnd, (IsPrimitiveInteger(styp) || IsPrimitiveVectorInteger(styp)),
1898 GetPrimTypeBitSize(styp));
1899
1900 PrimType destType = pointedType->GetPrimType();
1901 if (destType == PTY_agg) {
1902 destType = PTY_a64;
1903 }
1904 if (IsPrimitiveVector(styp)) { /* a vector type */
1905 destType = styp;
1906 }
1907 DEBUG_ASSERT(stmt.Opnd(0) != nullptr, "null ptr check");
1908 MemOperand &memOpnd = CreateMemOpnd(destType, stmt, *stmt.Opnd(0), offset);
1909 auto dataSize = GetPrimTypeBitSize(destType);
1910 memOpnd = memOpnd.IsOffsetMisaligned(dataSize) ? ConstraintOffsetToSafeRegion(dataSize, memOpnd, nullptr) : memOpnd;
1911 if (isVolStore && memOpnd.GetAddrMode() == MemOperand::kAddrModeBOi) {
1912 memOrd = AArch64isa::kMoRelease;
1913 isVolStore = false;
1914 }
1915
1916 if (memOrd == AArch64isa::kMoNone) {
1917 SelectCopy(memOpnd, destType, srcOpnd, destType);
1918 } else {
1919 AArch64CGFunc::SelectStoreRelease(memOpnd, destType, srcOpnd, destType, memOrd, false);
1920 }
1921 if (GetCurBB() && GetCurBB()->GetLastMachineInsn()) {
1922 GetCurBB()->GetLastMachineInsn()->MarkAsAccessRefField(isRefField);
1923 }
1924 }
1925
SelectIassignoff(IassignoffNode & stmt)1926 void AArch64CGFunc::SelectIassignoff(IassignoffNode &stmt)
1927 {
1928 int32 offset = stmt.GetOffset();
1929 PrimType destType = stmt.GetPrimType();
1930
1931 MemOperand &memOpnd = CreateMemOpnd(destType, stmt, *stmt.GetBOpnd(0), offset);
1932 auto dataSize = GetPrimTypeBitSize(destType);
1933 memOpnd = memOpnd.IsOffsetMisaligned(dataSize) ? ConstraintOffsetToSafeRegion(dataSize, memOpnd, nullptr) : memOpnd;
1934 Operand *valOpnd = HandleExpr(stmt, *stmt.GetBOpnd(1));
1935 Operand &srcOpnd = LoadIntoRegister(*valOpnd, true, GetPrimTypeBitSize(destType));
1936 SelectCopy(memOpnd, destType, srcOpnd, destType);
1937 }
1938
GenLmbcFpMemOperand(int32 offset,uint32 byteSize,AArch64reg baseRegno)1939 MemOperand *AArch64CGFunc::GenLmbcFpMemOperand(int32 offset, uint32 byteSize, AArch64reg baseRegno)
1940 {
1941 MemOperand *memOpnd;
1942 RegOperand *rfp = &GetOrCreatePhysicalRegisterOperand(baseRegno, k64BitSize, kRegTyInt);
1943 uint32 bitlen = byteSize * kBitsPerByte;
1944 if (offset < 0 && offset < kNegative256BitSize) {
1945 RegOperand *baseOpnd = &CreateRegisterOperandOfType(PTY_a64);
1946 ImmOperand &immOpnd = CreateImmOperand(offset, k32BitSize, true);
1947 Insn &addInsn = GetInsnBuilder()->BuildInsn(MOP_xaddrri12, *baseOpnd, *rfp, immOpnd);
1948 GetCurBB()->AppendInsn(addInsn);
1949 OfstOperand *offsetOpnd = &CreateOfstOpnd(0, k32BitSize);
1950 memOpnd = &GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, bitlen, baseOpnd, nullptr, offsetOpnd, nullptr);
1951 } else {
1952 OfstOperand *offsetOpnd = &CreateOfstOpnd(static_cast<uint64>(static_cast<int64>(offset)), k32BitSize);
1953 memOpnd = &GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, bitlen, rfp, nullptr, offsetOpnd, nullptr);
1954 }
1955 memOpnd->SetStackMem(true);
1956 return memOpnd;
1957 }
1958
SelectIassignfpoff(IassignFPoffNode & stmt,Operand & opnd)1959 void AArch64CGFunc::SelectIassignfpoff(IassignFPoffNode &stmt, Operand &opnd)
1960 {
1961 int32 offset = stmt.GetOffset();
1962 PrimType primType = stmt.GetPrimType();
1963 MIRType *rType = GetLmbcCallReturnType();
1964 bool isPureFpStruct = false;
1965 uint32 numRegs = 0;
1966 if (rType && rType->GetPrimType() == PTY_agg && opnd.IsRegister() &&
1967 static_cast<RegOperand &>(opnd).IsPhysicalRegister()) {
1968 CHECK_FATAL(rType->GetSize() <= k16BitSize, "SelectIassignfpoff invalid agg size");
1969 uint32 fpSize;
1970 numRegs = FloatParamRegRequired(static_cast<MIRStructType *>(rType), fpSize);
1971 if (numRegs) {
1972 primType = (fpSize == k4ByteSize) ? PTY_f32 : PTY_f64;
1973 isPureFpStruct = true;
1974 }
1975 }
1976 uint32 byteSize = GetPrimTypeSize(primType);
1977 uint32 bitlen = byteSize * kBitsPerByte;
1978 if (isPureFpStruct) {
1979 for (uint32 i = 0; i < numRegs; ++i) {
1980 MemOperand *memOpnd = GenLmbcFpMemOperand(offset + static_cast<int32>(i * byteSize), byteSize);
1981 RegOperand &srcOpnd = GetOrCreatePhysicalRegisterOperand(AArch64reg(V0 + i), bitlen, kRegTyFloat);
1982 MOperator mOp = PickStInsn(bitlen, primType);
1983 Insn &store = GetInsnBuilder()->BuildInsn(mOp, srcOpnd, *memOpnd);
1984 GetCurBB()->AppendInsn(store);
1985 }
1986 } else {
1987 Operand &srcOpnd = LoadIntoRegister(opnd, primType);
1988 MemOperand *memOpnd = GenLmbcFpMemOperand(offset, byteSize);
1989 MOperator mOp = PickStInsn(bitlen, primType);
1990 Insn &store = GetInsnBuilder()->BuildInsn(mOp, srcOpnd, *memOpnd);
1991 GetCurBB()->AppendInsn(store);
1992 }
1993 }
1994
1995 /* Load and assign to a new register. To be moved to the correct call register OR stack
1996 location in LmbcSelectParmList */
SelectIassignspoff(PrimType pTy,int32 offset,Operand & opnd)1997 void AArch64CGFunc::SelectIassignspoff(PrimType pTy, int32 offset, Operand &opnd)
1998 {
1999 if (GetLmbcArgInfo() == nullptr) {
2000 LmbcArgInfo *p = memPool->New<LmbcArgInfo>(*GetFuncScopeAllocator());
2001 SetLmbcArgInfo(p);
2002 }
2003 uint32 byteLen = GetPrimTypeSize(pTy);
2004 uint32 bitLen = byteLen * kBitsPerByte;
2005 RegType regTy = GetRegTyFromPrimTy(pTy);
2006 int32 curRegArgs = GetLmbcArgsInRegs(regTy);
2007 if (curRegArgs < static_cast<int32>(k8ByteSize)) {
2008 RegOperand *res = &CreateVirtualRegisterOperand(NewVReg(regTy, byteLen));
2009 SelectCopy(*res, pTy, opnd, pTy);
2010 SetLmbcArgInfo(res, pTy, offset, 1);
2011 } else {
2012 /* Move into allocated space */
2013 Operand &memOpd = CreateMemOpnd(RSP, offset, byteLen);
2014 Operand ® = LoadIntoRegister(opnd, pTy);
2015 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(PickStInsn(bitLen, pTy), reg, memOpd));
2016 }
2017 IncLmbcArgsInRegs(regTy); /* num of args in registers */
2018 IncLmbcTotalArgs(); /* num of args */
2019 }
2020
2021 /* Search for CALL/ICALL/ICALLPROTO node, must be called from a blkassignoff node */
LmbcGetAggTyFromCallSite(StmtNode * stmt,std::vector<TyIdx> ** parmList) const2022 MIRType *AArch64CGFunc::LmbcGetAggTyFromCallSite(StmtNode *stmt, std::vector<TyIdx> **parmList) const
2023 {
2024 for (; stmt != nullptr; stmt = stmt->GetNext()) {
2025 if (stmt->GetOpCode() == OP_call || stmt->GetOpCode() == OP_icallproto) {
2026 break;
2027 }
2028 }
2029 CHECK_FATAL(stmt && (stmt->GetOpCode() == OP_call || stmt->GetOpCode() == OP_icallproto),
2030 "blkassign sp not followed by call");
2031 uint32 nargs = GetLmbcTotalArgs();
2032 MIRType *ty = nullptr;
2033 if (stmt->GetOpCode() == OP_call) {
2034 CallNode *callNode = static_cast<CallNode *>(stmt);
2035 MIRFunction *fn = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(callNode->GetPUIdx());
2036 if (fn->GetFormalCount() > 0) {
2037 ty = GlobalTables::GetTypeTable().GetTypeFromTyIdx(fn->GetFormalDefVec()[nargs].formalTyIdx);
2038 }
2039 *parmList = &fn->GetParamTypes();
2040 // would return null if the actual parameter is bogus
2041 } else if (stmt->GetOpCode() == OP_icallproto) {
2042 IcallNode *icallproto = static_cast<IcallNode *>(stmt);
2043 MIRType *type = GlobalTables::GetTypeTable().GetTypeFromTyIdx(icallproto->GetRetTyIdx());
2044 MIRFuncType *fType = static_cast<MIRFuncType *>(type);
2045 ty = GlobalTables::GetTypeTable().GetTypeFromTyIdx(fType->GetNthParamType(nargs));
2046 *parmList = &fType->GetParamTypeList();
2047 } else {
2048 CHECK_FATAL(stmt->GetOpCode() == OP_icallproto, "LmbcGetAggTyFromCallSite:: unexpected call operator");
2049 }
2050 return ty;
2051 }
2052
2053 /* return true if blkassignoff for return, false otherwise */
LmbcSmallAggForRet(const BaseNode & bNode,const Operand * src,int32 offset,bool skip1)2054 bool AArch64CGFunc::LmbcSmallAggForRet(const BaseNode &bNode, const Operand *src, int32 offset, bool skip1)
2055 {
2056 PrimType pTy;
2057 uint32 size = 0;
2058 AArch64reg regno = static_cast<AArch64reg>(static_cast<const RegOperand *>(src)->GetRegisterNumber());
2059 MIRFunction *func = &GetFunction();
2060
2061 if (!func->IsReturnStruct()) {
2062 return false;
2063 }
2064 /* This blkassignoff is for struct return? */
2065 uint32 loadSize;
2066 uint32 numRegs = 0;
2067 if (static_cast<const StmtNode &>(bNode).GetNext()->GetOpCode() == OP_return) {
2068 MIRStructType *ty = static_cast<MIRStructType *>(func->GetReturnType());
2069 uint32 tySize = GetBecommon().GetTypeSize(ty->GetTypeIndex());
2070 uint32 fpregs = FloatParamRegRequired(ty, size);
2071 if (fpregs > 0) {
2072 /* pure floating point in agg */
2073 numRegs = fpregs;
2074 pTy = (size == k4ByteSize) ? PTY_f32 : PTY_f64;
2075 loadSize = GetPrimTypeSize(pTy) * kBitsPerByte;
2076 for (uint32 i = 0; i < fpregs; i++) {
2077 int32 s = (i == 0) ? 0 : static_cast<int32>(i * size);
2078 int64 newOffset = static_cast<int64>(s) + static_cast<int64>(offset);
2079 MemOperand &mem = CreateMemOpnd(regno, newOffset, size * kBitsPerByte);
2080 AArch64reg reg = static_cast<AArch64reg>(V0 + i);
2081 RegOperand *res = &GetOrCreatePhysicalRegisterOperand(reg, loadSize, kRegTyFloat);
2082 SelectCopy(*res, pTy, mem, pTy);
2083 }
2084 } else {
2085 constexpr uint8 numRegMax = 2;
2086 /* int/float mixed */
2087 numRegs = numRegMax;
2088 pTy = PTY_i64;
2089 constexpr uint32 oneByte = 1;
2090 constexpr uint32 twoByte = 2;
2091 constexpr uint32 fourByte = 4;
2092 size = k4ByteSize;
2093 switch (tySize) {
2094 case oneByte:
2095 pTy = PTY_i8;
2096 break;
2097 case twoByte:
2098 pTy = PTY_i16;
2099 break;
2100 case fourByte:
2101 pTy = PTY_i32;
2102 break;
2103 default:
2104 size = k8ByteSize; /* pTy remains i64 */
2105 break;
2106 }
2107 loadSize = GetPrimTypeSize(pTy) * kBitsPerByte;
2108 if (!skip1) {
2109 MemOperand &mem = CreateMemOpnd(regno, offset, size * kBitsPerByte);
2110 RegOperand &res1 = GetOrCreatePhysicalRegisterOperand(R0, loadSize, kRegTyInt);
2111 SelectCopy(res1, pTy, mem, pTy);
2112 }
2113 if (tySize > k8ByteSize) {
2114 int32 newOffset = offset + static_cast<int32>(k8ByteSize);
2115 MemOperand &newMem = CreateMemOpnd(regno, newOffset, size * kBitsPerByte);
2116 RegOperand &res2 = GetOrCreatePhysicalRegisterOperand(R1, loadSize, kRegTyInt);
2117 SelectCopy(res2, pTy, newMem, pTy);
2118 }
2119 }
2120 bool intReg = fpregs == 0;
2121 for (uint32 i = 0; i < numRegs; i++) {
2122 AArch64reg preg = static_cast<AArch64reg>((intReg ? R0 : V0) + i);
2123 MOperator mop = intReg ? MOP_pseudo_ret_int : MOP_pseudo_ret_float;
2124 RegOperand &dest = GetOrCreatePhysicalRegisterOperand(preg, loadSize, intReg ? kRegTyInt : kRegTyFloat);
2125 Insn &pseudo = GetInsnBuilder()->BuildInsn(mop, dest);
2126 GetCurBB()->AppendInsn(pseudo);
2127 }
2128 return true;
2129 }
2130 return false;
2131 }
2132
2133 /* return true if blkassignoff for return, false otherwise */
LmbcSmallAggForCall(BlkassignoffNode & bNode,const Operand * src,std::vector<TyIdx> ** parmList)2134 bool AArch64CGFunc::LmbcSmallAggForCall(BlkassignoffNode &bNode, const Operand *src, std::vector<TyIdx> **parmList)
2135 {
2136 AArch64reg regno = static_cast<AArch64reg>(static_cast<const RegOperand *>(src)->GetRegisterNumber());
2137 if (IsBlkassignForPush(bNode)) {
2138 PrimType pTy = PTY_i64;
2139 MIRStructType *ty = static_cast<MIRStructType *>(LmbcGetAggTyFromCallSite(&bNode, parmList));
2140 uint32 size = 0;
2141 uint32 fpregs = ty ? FloatParamRegRequired(ty, size) : 0; /* fp size determined */
2142 if (fpregs > 0) {
2143 /* pure floating point in agg */
2144 pTy = (size == k4ByteSize) ? PTY_f32 : PTY_f64;
2145 for (uint32 i = 0; i < fpregs; i++) {
2146 int32 s = (i == 0) ? 0 : static_cast<int32>(i * size);
2147 MemOperand &mem = CreateMemOpnd(regno, s, size * kBitsPerByte);
2148 RegOperand *res = &CreateVirtualRegisterOperand(NewVReg(kRegTyFloat, size));
2149 SelectCopy(*res, pTy, mem, pTy);
2150 SetLmbcArgInfo(res, pTy, 0, static_cast<int32>(fpregs));
2151 IncLmbcArgsInRegs(kRegTyFloat);
2152 }
2153 IncLmbcTotalArgs();
2154 return true;
2155 } else if (bNode.blockSize <= static_cast<int32>(k16ByteSize)) {
2156 /* integer/mixed types in register/s */
2157 size = k4ByteSize;
2158 switch (bNode.blockSize) {
2159 case k1ByteSize:
2160 pTy = PTY_i8;
2161 break;
2162 case k2ByteSize:
2163 pTy = PTY_i16;
2164 break;
2165 case k4ByteSize:
2166 pTy = PTY_i32;
2167 break;
2168 default:
2169 size = k8ByteSize; /* pTy remains i64 */
2170 break;
2171 }
2172 MemOperand &mem = CreateMemOpnd(regno, 0, size * kBitsPerByte);
2173 RegOperand *res = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, size));
2174 SelectCopy(*res, pTy, mem, pTy);
2175 SetLmbcArgInfo(res, pTy, bNode.offset,
2176 bNode.blockSize > static_cast<int32>(k8ByteSize) ? kThirdReg : kSecondReg);
2177 IncLmbcArgsInRegs(kRegTyInt);
2178 if (bNode.blockSize > static_cast<int32>(k8ByteSize)) {
2179 MemOperand &newMem = CreateMemOpnd(regno, k8ByteSize, size * kBitsPerByte);
2180 RegOperand *newRes = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, size));
2181 SelectCopy(*newRes, pTy, newMem, pTy);
2182 SetLmbcArgInfo(newRes, pTy, bNode.offset + k8ByteSizeInt, kThirdReg);
2183 IncLmbcArgsInRegs(kRegTyInt);
2184 }
2185 IncLmbcTotalArgs();
2186 return true;
2187 }
2188 }
2189 return false;
2190 }
2191
2192 /* This function is incomplete and may be removed when Lmbc IR is changed
2193 to have the lowerer figures out the address of the large agg to reside */
LmbcFindTotalStkUsed(std::vector<TyIdx> * paramList)2194 uint32 AArch64CGFunc::LmbcFindTotalStkUsed(std::vector<TyIdx> *paramList)
2195 {
2196 AArch64CallConvImpl parmlocator(GetBecommon());
2197 CCLocInfo pLoc;
2198 for (TyIdx tyIdx : *paramList) {
2199 MIRType *ty = GlobalTables::GetTypeTable().GetTypeFromTyIdx(tyIdx);
2200 (void)parmlocator.LocateNextParm(*ty, pLoc);
2201 }
2202 return 0;
2203 }
2204
2205 /* All arguments passed as registers */
LmbcTotalRegsUsed()2206 uint32 AArch64CGFunc::LmbcTotalRegsUsed()
2207 {
2208 if (GetLmbcArgInfo() == nullptr) {
2209 return 0; /* no arg */
2210 }
2211 MapleVector<int32> ®s = GetLmbcCallArgNumOfRegs();
2212 MapleVector<PrimType> &types = GetLmbcCallArgTypes();
2213 uint32 iCnt = 0;
2214 uint32 fCnt = 0;
2215 for (uint32 i = 0; i < regs.size(); i++) {
2216 if (IsPrimitiveInteger(types[i])) {
2217 if ((iCnt + static_cast<uint32>(regs[i])) <= k8ByteSize) {
2218 iCnt += static_cast<uint32>(regs[i]);
2219 };
2220 } else {
2221 if ((fCnt + static_cast<uint32>(regs[i])) <= k8ByteSize) {
2222 fCnt += static_cast<uint32>(regs[i]);
2223 };
2224 }
2225 }
2226 return iCnt + fCnt;
2227 }
2228
2229 /* If blkassignoff for argument, this function loads the agg arguments into
2230 virtual registers, disregard if there is sufficient physicall call
2231 registers. Argument > 16-bytes are copied to preset space and ptr
2232 result is loaded into virtual register.
2233 If blassign is not for argument, this function simply memcpy */
SelectBlkassignoff(BlkassignoffNode & bNode,Operand * src)2234 void AArch64CGFunc::SelectBlkassignoff(BlkassignoffNode &bNode, Operand *src)
2235 {
2236 CHECK_FATAL(src->GetKind() == Operand::kOpdRegister, "blkassign src type not in register");
2237 std::vector<TyIdx> *parmList;
2238 if (GetLmbcArgInfo() == nullptr) {
2239 LmbcArgInfo *p = memPool->New<LmbcArgInfo>(*GetFuncScopeAllocator());
2240 SetLmbcArgInfo(p);
2241 }
2242 if (LmbcSmallAggForRet(bNode, src)) {
2243 return;
2244 } else if (LmbcSmallAggForCall(bNode, src, &parmList)) {
2245 return;
2246 }
2247 Operand *dest = HandleExpr(bNode, *bNode.Opnd(0));
2248 RegOperand *regResult = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
2249 /* memcpy for agg assign OR large agg for arg/ret */
2250 int32 offset = bNode.offset;
2251 if (IsBlkassignForPush(bNode)) {
2252 /* large agg for call, addr to be pushed in SelectCall */
2253 offset = GetLmbcTotalStkUsed();
2254 if (offset < 0) {
2255 /* length of ALL stack based args for this call, this location is where the
2256 next large agg resides, its addr will then be passed */
2257 offset = static_cast<int32_t>(LmbcFindTotalStkUsed(parmList) + LmbcTotalRegsUsed());
2258 }
2259 SetLmbcTotalStkUsed(offset + bNode.blockSize); /* next use */
2260 SetLmbcArgInfo(regResult, PTY_i64, 0, 1); /* 1 reg for ptr */
2261 IncLmbcArgsInRegs(kRegTyInt);
2262 IncLmbcTotalArgs();
2263 /* copy large agg arg to offset below */
2264 }
2265 std::vector<Operand *> opndVec;
2266 opndVec.push_back(regResult); /* result */
2267 opndVec.push_back(PrepareMemcpyParamOpnd(offset, *dest)); /* param 0 */
2268 opndVec.push_back(src); /* param 1 */
2269 opndVec.push_back(PrepareMemcpyParamOpnd(static_cast<uint64>(static_cast<int64>(bNode.blockSize)))); /* param 2 */
2270 SelectLibCall("memcpy", opndVec, PTY_a64, PTY_a64);
2271 }
2272
SelectAggIassign(IassignNode & stmt,Operand & addrOpnd)2273 void AArch64CGFunc::SelectAggIassign(IassignNode &stmt, Operand &addrOpnd)
2274 {
2275 DEBUG_ASSERT(stmt.Opnd(0) != nullptr, "null ptr check");
2276 auto &lhsAddrOpnd = LoadIntoRegister(addrOpnd, stmt.Opnd(0)->GetPrimType());
2277 MemRWNodeHelper lhsMemHelper(stmt, GetFunction(), GetBecommon());
2278 auto &lhsOfstOpnd = CreateImmOperand(lhsMemHelper.GetByteOffset(), k32BitSize, false);
2279 auto *lhsMemOpnd = CreateMemOperand(k64BitSize, lhsAddrOpnd, lhsOfstOpnd, stmt.AssigningVolatile());
2280
2281 bool isRefField = false;
2282 MemOperand *rhsMemOpnd = SelectRhsMemOpnd(*stmt.GetRHS(), isRefField);
2283 SelectMemCopy(*lhsMemOpnd, *rhsMemOpnd, lhsMemHelper.GetMemSize(), isRefField, &stmt, stmt.GetRHS());
2284 }
2285
SelectReturnSendOfStructInRegs(BaseNode * x)2286 void AArch64CGFunc::SelectReturnSendOfStructInRegs(BaseNode *x)
2287 {
2288 if (x->GetOpCode() != OP_dread && x->GetOpCode() != OP_iread) {
2289 // dummy return of 0 inserted by front-end at absence of return
2290 DEBUG_ASSERT(x->GetOpCode() == OP_constval, "NIY: unexpected return operand");
2291 uint32 typeSize = GetPrimTypeBitSize(x->GetPrimType());
2292 RegOperand &dest = GetOrCreatePhysicalRegisterOperand(R0, typeSize, kRegTyInt);
2293 ImmOperand &src = CreateImmOperand(0, typeSize, false);
2294 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wmovri32, dest, src));
2295 return;
2296 }
2297
2298 MemRWNodeHelper helper(*x, GetFunction(), GetBecommon());
2299 bool isRefField = false;
2300 auto *addrOpnd = SelectRhsMemOpnd(*x, isRefField);
2301
2302 // generate move to regs for agg return
2303 AArch64CallConvImpl parmlocator(GetBecommon());
2304 CCLocInfo retMatch;
2305 parmlocator.LocateRetVal(*helper.GetMIRType(), retMatch);
2306 if (retMatch.GetReg0() == kRinvalid) {
2307 return;
2308 }
2309
2310 uint32 offset = 0;
2311 std::vector<RegOperand *> result;
2312 // load memOpnd to return reg
2313 // ldr x0, [base]
2314 // ldr x1, [base + 8]
2315 auto generateReturnValToRegs = [this, &result, &offset, isRefField, &addrOpnd](regno_t regno, PrimType primType) {
2316 bool isFpReg = IsPrimitiveFloat(primType) || IsPrimitiveVector(primType);
2317 RegType regType = isFpReg ? kRegTyFloat : kRegTyInt;
2318 if (CGOptions::IsBigEndian() && !isFpReg) {
2319 primType = PTY_u64;
2320 } else if (GetPrimTypeSize(primType) <= k4ByteSize) {
2321 primType = isFpReg ? PTY_f32 : PTY_u32;
2322 }
2323 auto &phyReg =
2324 GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(regno), GetPrimTypeBitSize(primType), regType);
2325 DEBUG_ASSERT(addrOpnd->IsMemoryAccessOperand(), "NIY, must be mem opnd");
2326 auto *newMemOpnd =
2327 &GetMemOperandAddOffset(static_cast<MemOperand &>(*addrOpnd), offset, GetPrimTypeBitSize(primType));
2328 MOperator ldMop = PickLdInsn(GetPrimTypeBitSize(primType), primType);
2329 Insn &ldInsn = GetInsnBuilder()->BuildInsn(ldMop, phyReg, *newMemOpnd);
2330 ldInsn.MarkAsAccessRefField(isRefField);
2331 GetCurBB()->AppendInsn(ldInsn);
2332 if (!VERIFY_INSN(&ldInsn)) {
2333 SPLIT_INSN(&ldInsn, this);
2334 }
2335
2336 offset += GetPrimTypeSize(primType);
2337 result.push_back(&phyReg);
2338 };
2339 generateReturnValToRegs(retMatch.GetReg0(), retMatch.GetPrimTypeOfReg0());
2340 if (retMatch.GetReg1() != kRinvalid) {
2341 generateReturnValToRegs(retMatch.GetReg1(), retMatch.GetPrimTypeOfReg1());
2342 }
2343 if (retMatch.GetReg2() != kRinvalid) {
2344 generateReturnValToRegs(retMatch.GetReg2(), retMatch.GetPrimTypeOfReg2());
2345 }
2346 if (retMatch.GetReg3() != kRinvalid) {
2347 generateReturnValToRegs(retMatch.GetReg3(), retMatch.GetPrimTypeOfReg3());
2348 }
2349 }
2350
SelectDread(const BaseNode & parent,DreadNode & expr)2351 Operand *AArch64CGFunc::SelectDread(const BaseNode &parent, DreadNode &expr)
2352 {
2353 MIRSymbol *symbol = GetFunction().GetLocalOrGlobalSymbol(expr.GetStIdx());
2354 auto itr = stIdx2OverflowResult.find(expr.GetStIdx());
2355 if (itr != stIdx2OverflowResult.end()) {
2356 /* add_with_overflow / sub_with_overflow:
2357 * reg1: param1
2358 * reg2: param2
2359 * adds/subs reg3, reg1, reg2
2360 * cset reg4, vs
2361 * result is saved in std::pair<RegOperand*, RegOperand*>(reg3, reg4)
2362 */
2363 if (expr.GetFieldID() == 1) {
2364 return itr->second.first;
2365 } else {
2366 DEBUG_ASSERT(expr.GetFieldID() == 2, "only has 2 fields for intrinsic overflow call result"); // 2 fields
2367 return itr->second.second;
2368 }
2369 }
2370 if (symbol->IsEhIndex()) {
2371 CHECK_FATAL(false, "should not go here");
2372 MIRType *type = GlobalTables::GetTypeTable().GetTypeFromTyIdx(static_cast<TyIdx>(PTY_i32));
2373 /* use the second register return by __builtin_eh_return(). */
2374 AArch64CallConvImpl retLocator(GetBecommon());
2375 CCLocInfo retMech;
2376 retLocator.LocateRetVal(*type, retMech);
2377 retLocator.SetupSecondRetReg(*type, retMech);
2378 return &GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(retMech.GetReg1()), k64BitSize, kRegTyInt);
2379 }
2380
2381 PrimType symType = symbol->GetType()->GetPrimType();
2382 uint32 offset = 0;
2383 bool parmCopy = false;
2384 if (expr.GetFieldID() != 0) {
2385 MIRStructType *structType = static_cast<MIRStructType *>(symbol->GetType());
2386 DEBUG_ASSERT(structType != nullptr, "SelectDread: non-zero fieldID for non-structure");
2387 symType = structType->GetFieldType(expr.GetFieldID())->GetPrimType();
2388 offset = static_cast<uint32>(GetBecommon().GetFieldOffset(*structType, expr.GetFieldID()).first);
2389 parmCopy = IsParamStructCopy(*symbol);
2390 }
2391
2392 uint32 dataSize = GetPrimTypeBitSize(symType);
2393 uint32 aggSize = 0;
2394 PrimType resultType = expr.GetPrimType();
2395 if (symType == PTY_agg) {
2396 if (expr.GetPrimType() == PTY_agg) {
2397 aggSize = static_cast<uint32>(GetBecommon().GetTypeSize(symbol->GetType()->GetTypeIndex().GetIdx()));
2398 dataSize = ((expr.GetFieldID() == 0) ? GetPointerSize() : aggSize) << k8BitShift;
2399 resultType = PTY_u64;
2400 symType = resultType;
2401 } else {
2402 dataSize = GetPrimTypeBitSize(expr.GetPrimType());
2403 }
2404 }
2405 MemOperand *memOpnd = nullptr;
2406 if (aggSize > k8ByteSize) {
2407 if (parent.op == OP_eval) {
2408 if (symbol->GetAttr(ATTR_volatile)) {
2409 /* Need to generate loads for the upper parts of the struct. */
2410 Operand &dest = GetZeroOpnd(k64BitSize);
2411 uint32 numLoads = static_cast<uint32>(RoundUp(aggSize, k64BitSize) / k64BitSize);
2412 for (uint32 o = 0; o < numLoads; ++o) {
2413 if (parmCopy) {
2414 memOpnd = &LoadStructCopyBase(*symbol, offset + o * GetPointerSize(), GetPointerSize());
2415 } else {
2416 memOpnd = &GetOrCreateMemOpnd(*symbol, offset + o * GetPointerSize(), GetPointerSize());
2417 }
2418 if (IsImmediateOffsetOutOfRange(*memOpnd, GetPointerSize())) {
2419 memOpnd = &SplitOffsetWithAddInstruction(*memOpnd, GetPointerSize());
2420 }
2421 SelectCopy(dest, PTY_u64, *memOpnd, PTY_u64);
2422 }
2423 } else {
2424 /* No side-effects. No need to generate anything for eval. */
2425 }
2426 } else {
2427 if (expr.GetFieldID() != 0) {
2428 CHECK_FATAL(false, "SelectDread: Illegal agg size");
2429 }
2430 }
2431 }
2432 if (parmCopy) {
2433 memOpnd = &LoadStructCopyBase(*symbol, offset, static_cast<int>(dataSize));
2434 } else {
2435 memOpnd = &GetOrCreateMemOpnd(*symbol, offset, dataSize);
2436 }
2437 if ((memOpnd->GetMemVaryType() == kNotVary) && IsImmediateOffsetOutOfRange(*memOpnd, dataSize)) {
2438 memOpnd = &SplitOffsetWithAddInstruction(*memOpnd, dataSize);
2439 }
2440
2441 RegOperand &resOpnd = GetOrCreateResOperand(parent, symType);
2442 /* a local register variable defined with a specified register */
2443 if (symbol->GetAsmAttr() != UStrIdx(0) && symbol->GetStorageClass() != kScPstatic &&
2444 symbol->GetStorageClass() != kScFstatic) {
2445 std::string regDesp = GlobalTables::GetUStrTable().GetStringFromStrIdx(symbol->GetAsmAttr());
2446 RegOperand &specifiedOpnd = GetOrCreatePhysicalRegisterOperand(regDesp);
2447 return &specifiedOpnd;
2448 }
2449 memOpnd =
2450 memOpnd->IsOffsetMisaligned(dataSize) ? &ConstraintOffsetToSafeRegion(dataSize, *memOpnd, symbol) : memOpnd;
2451 SelectCopy(resOpnd, resultType, *memOpnd, symType);
2452 return &resOpnd;
2453 }
2454
SelectRegread(RegreadNode & expr)2455 RegOperand *AArch64CGFunc::SelectRegread(RegreadNode &expr)
2456 {
2457 PregIdx pregIdx = expr.GetRegIdx();
2458 if (IsSpecialPseudoRegister(pregIdx)) {
2459 /* if it is one of special registers */
2460 return &GetOrCreateSpecialRegisterOperand(-pregIdx, expr.GetPrimType());
2461 }
2462 RegOperand ® = GetOrCreateVirtualRegisterOperand(GetVirtualRegNOFromPseudoRegIdx(pregIdx));
2463 if (GetOpndFromPregIdx(pregIdx) == nullptr) {
2464 SetPregIdx2Opnd(pregIdx, reg);
2465 }
2466 if (expr.GetPrimType() == PTY_ref) {
2467 reg.SetIsReference(true);
2468 AddReferenceReg(reg.GetRegisterNumber());
2469 }
2470 if (Globals::GetInstance()->GetOptimLevel() == CGOptions::kLevel0) {
2471 MemOperand *src = GetPseudoRegisterSpillMemoryOperand(pregIdx);
2472 MIRPreg *preg = GetFunction().GetPregTab()->PregFromPregIdx(pregIdx);
2473 PrimType stype = preg->GetPrimType();
2474 uint32 srcBitLength = GetPrimTypeSize(stype) * kBitsPerByte;
2475 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(PickLdInsn(srcBitLength, stype), reg, *src));
2476 }
2477 return ®
2478 }
2479
SelectAddrof(Operand & result,StImmOperand & stImm,FieldID field)2480 void AArch64CGFunc::SelectAddrof(Operand &result, StImmOperand &stImm, FieldID field)
2481 {
2482 const MIRSymbol *symbol = stImm.GetSymbol();
2483 if (symbol->GetStorageClass() == kScAuto) {
2484 SetStackProtectInfo(kAddrofStack);
2485 }
2486 if ((symbol->GetStorageClass() == kScAuto) || (symbol->GetStorageClass() == kScFormal)) {
2487 if (!CGOptions::IsQuiet()) {
2488 maple::LogInfo::MapleLogger(kLlErr)
2489 << "Warning: we expect AddrOf with StImmOperand is not used for local variables";
2490 }
2491 AArch64SymbolAlloc *symLoc =
2492 static_cast<AArch64SymbolAlloc *>(GetMemlayout()->GetSymAllocInfo(symbol->GetStIndex()));
2493 ImmOperand *offset = nullptr;
2494 if (symLoc->GetMemSegment()->GetMemSegmentKind() == kMsArgsStkPassed) {
2495 offset = &CreateImmOperand(GetBaseOffset(*symLoc) + stImm.GetOffset(), k64BitSize, false, kUnAdjustVary);
2496 } else if (symLoc->GetMemSegment()->GetMemSegmentKind() == kMsRefLocals) {
2497 auto it = immOpndsRequiringOffsetAdjustmentForRefloc.find(symLoc);
2498 if (it != immOpndsRequiringOffsetAdjustmentForRefloc.end()) {
2499 offset = (*it).second;
2500 } else {
2501 offset = &CreateImmOperand(GetBaseOffset(*symLoc) + stImm.GetOffset(), k64BitSize, false);
2502 immOpndsRequiringOffsetAdjustmentForRefloc[symLoc] = offset;
2503 }
2504 } else {
2505 /* Do not cache modified symbol location */
2506 offset = &CreateImmOperand(GetBaseOffset(*symLoc) + stImm.GetOffset(), k64BitSize, false);
2507 }
2508
2509 SelectAdd(result, *GetBaseReg(*symLoc), *offset, PTY_u64);
2510 if (GetCG()->GenerateVerboseCG()) {
2511 /* Add a comment */
2512 Insn *insn = GetCurBB()->GetLastInsn();
2513 std::string comm = "local/formal var: ";
2514 comm += symbol->GetName();
2515 insn->SetComment(comm);
2516 }
2517 } else if (symbol->IsThreadLocal()) {
2518 SelectAddrofThreadLocal(result, stImm);
2519 return;
2520 } else {
2521 Operand *srcOpnd = &result;
2522 if (!IsAfterRegAlloc()) {
2523 // Create a new vreg/preg for the upper bits of the address
2524 PregIdx pregIdx = GetFunction().GetPregTab()->CreatePreg(PTY_a64);
2525 MIRPreg *tmpPreg = GetFunction().GetPregTab()->PregFromPregIdx(pregIdx);
2526 regno_t vRegNO = NewVReg(kRegTyInt, GetPrimTypeSize(PTY_a64));
2527 RegOperand &tmpreg = GetOrCreateVirtualRegisterOperand(vRegNO);
2528
2529 // Register this vreg mapping
2530 RegisterVregMapping(vRegNO, pregIdx);
2531
2532 // Store rematerialization info in the preg
2533 tmpPreg->SetOp(OP_addrof);
2534 tmpPreg->rematInfo.sym = symbol;
2535 tmpPreg->fieldID = field;
2536 tmpPreg->addrUpper = true;
2537
2538 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xadrp, tmpreg, stImm));
2539 srcOpnd = &tmpreg;
2540 } else {
2541 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xadrp, result, stImm));
2542 }
2543 if (CGOptions::IsPIC() && symbol->NeedPIC()) {
2544 OfstOperand &offset = CreateOfstOpnd(*stImm.GetSymbol(), stImm.GetOffset(), stImm.GetRelocs());
2545
2546 auto size = GetPointerSize() * kBitsPerByte;
2547 MemOperand &memOpnd = GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, size, static_cast<RegOperand *>(srcOpnd),
2548 nullptr, &offset, nullptr);
2549 GetCurBB()->AppendInsn(
2550 GetInsnBuilder()->BuildInsn(size == k64BitSize ? MOP_xldr : MOP_wldr, result, memOpnd));
2551
2552 if (stImm.GetOffset() > 0) {
2553 ImmOperand &immOpnd = CreateImmOperand(stImm.GetOffset(), result.GetSize(), false);
2554 SelectAdd(result, result, immOpnd, PTY_u64);
2555 }
2556 } else {
2557 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xadrpl12, result, *srcOpnd, stImm));
2558 }
2559 }
2560 }
2561
SelectAddrof(Operand & result,MemOperand & memOpnd,FieldID field)2562 void AArch64CGFunc::SelectAddrof(Operand &result, MemOperand &memOpnd, FieldID field)
2563 {
2564 const MIRSymbol *symbol = memOpnd.GetSymbol();
2565 if (symbol->GetStorageClass() == kScAuto) {
2566 auto *offsetOpnd = static_cast<OfstOperand *>(memOpnd.GetOffsetImmediate());
2567 Operand &immOpnd = CreateImmOperand(offsetOpnd->GetOffsetValue(), PTY_u32, false);
2568 DEBUG_ASSERT(memOpnd.GetBaseRegister() != nullptr, "nullptr check");
2569 SelectAdd(result, *memOpnd.GetBaseRegister(), immOpnd, PTY_u32);
2570 SetStackProtectInfo(kAddrofStack);
2571 } else if (!IsAfterRegAlloc()) {
2572 // Create a new vreg/preg for the upper bits of the address
2573 PregIdx pregIdx = GetFunction().GetPregTab()->CreatePreg(PTY_a64);
2574 MIRPreg *tmpPreg = GetFunction().GetPregTab()->PregFromPregIdx(pregIdx);
2575 regno_t vRegNO = NewVReg(kRegTyInt, GetPrimTypeSize(PTY_a64));
2576 RegOperand &tmpreg = GetOrCreateVirtualRegisterOperand(vRegNO);
2577
2578 // Register this vreg mapping
2579 RegisterVregMapping(vRegNO, pregIdx);
2580
2581 // Store rematerialization info in the preg
2582 tmpPreg->SetOp(OP_addrof);
2583 tmpPreg->rematInfo.sym = symbol;
2584 tmpPreg->fieldID = field;
2585 tmpPreg->addrUpper = true;
2586
2587 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xadrp, tmpreg, memOpnd));
2588 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xadrpl12, result, tmpreg, memOpnd));
2589 } else {
2590 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xadrp, result, memOpnd));
2591 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xadrpl12, result, result, memOpnd));
2592 }
2593 }
2594
SelectAddrof(AddrofNode & expr,const BaseNode & parent,bool isAddrofoff)2595 Operand *AArch64CGFunc::SelectAddrof(AddrofNode &expr, const BaseNode &parent, bool isAddrofoff)
2596 {
2597 MIRSymbol *symbol = GetFunction().GetLocalOrGlobalSymbol(expr.GetStIdx());
2598 DEBUG_ASSERT(symbol != nullptr, "symbol should not be nullptr");
2599 int32 offset = 0;
2600 AddrofoffNode &addrofoffExpr = static_cast<AddrofoffNode &>(static_cast<BaseNode &>(expr));
2601 if (isAddrofoff) {
2602 offset = addrofoffExpr.offset;
2603 } else {
2604 if (expr.GetFieldID() != 0) {
2605 MIRStructType *structType = static_cast<MIRStructType *>(symbol->GetType());
2606 /* with array of structs, it is possible to have nullptr */
2607 if (structType != nullptr) {
2608 offset = GetBecommon().GetFieldOffset(*structType, expr.GetFieldID()).first;
2609 }
2610 }
2611 }
2612 if ((symbol->GetStorageClass() == kScFormal) && (symbol->GetSKind() == kStVar) &&
2613 ((!isAddrofoff && expr.GetFieldID() != 0) ||
2614 (GetBecommon().GetTypeSize(symbol->GetType()->GetTypeIndex().GetIdx()) > k16ByteSize))) {
2615 /*
2616 * Struct param is copied on the stack by caller if struct size > 16.
2617 * Else if size < 16 then struct param is copied into one or two registers.
2618 */
2619 RegOperand *stackAddr = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
2620 /* load the base address of the struct copy from stack. */
2621 SelectAddrof(*stackAddr, CreateStImmOperand(*symbol, 0, 0));
2622 Operand *structAddr;
2623 if (!IsParamStructCopy(*symbol)) {
2624 isAggParamInReg = true;
2625 structAddr = stackAddr;
2626 } else {
2627 OfstOperand *offopnd = &CreateOfstOpnd(0, k32BitSize);
2628 MemOperand *mo = &GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, GetPointerSize() * kBitsPerByte, stackAddr,
2629 nullptr, offopnd, nullptr);
2630 structAddr = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
2631 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xldr, *structAddr, *mo));
2632 }
2633 if (offset == 0) {
2634 return structAddr;
2635 } else {
2636 /* add the struct offset to the base address */
2637 Operand *result = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
2638 ImmOperand *imm = &CreateImmOperand(PTY_a64, offset);
2639 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xaddrri12, *result, *structAddr, *imm));
2640 return result;
2641 }
2642 }
2643 PrimType ptype = expr.GetPrimType();
2644 Operand &result = GetOrCreateResOperand(parent, ptype);
2645 if (symbol->IsReflectionClassInfo() && !symbol->IsReflectionArrayClassInfo() && !GetCG()->IsLibcore()) {
2646 std::string ptrName = namemangler::kPtrPrefixStr + symbol->GetName();
2647 MIRType *ptrType = GlobalTables::GetTypeTable().GetPtr();
2648 symbol = GetMirModule().GetMIRBuilder()->GetOrCreateGlobalDecl(ptrName, *ptrType);
2649 symbol->SetStorageClass(kScFstatic);
2650
2651 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_adrp_ldr, result, CreateStImmOperand(*symbol, 0, 0)));
2652 /* make it un rematerializable. */
2653 MIRPreg *preg = GetPseudoRegFromVirtualRegNO(static_cast<RegOperand &>(result).GetRegisterNumber());
2654 if (preg) {
2655 preg->SetOp(OP_undef);
2656 }
2657 return &result;
2658 }
2659
2660 SelectAddrof(result, CreateStImmOperand(*symbol, offset, 0), isAddrofoff ? 0 : expr.GetFieldID());
2661 return &result;
2662 }
2663
SelectAddrofoff(AddrofoffNode & expr,const BaseNode & parent)2664 Operand *AArch64CGFunc::SelectAddrofoff(AddrofoffNode &expr, const BaseNode &parent)
2665 {
2666 return SelectAddrof(static_cast<AddrofNode &>(static_cast<BaseNode &>(expr)), parent, true);
2667 }
2668
SelectAddrofFunc(AddroffuncNode & expr,const BaseNode & parent)2669 Operand &AArch64CGFunc::SelectAddrofFunc(AddroffuncNode &expr, const BaseNode &parent)
2670 {
2671 uint32 instrSize = static_cast<uint32>(expr.SizeOfInstr());
2672 PrimType primType = (instrSize == k8ByteSize)
2673 ? PTY_u64
2674 : (instrSize == k4ByteSize) ? PTY_u32 : (instrSize == k2ByteSize) ? PTY_u16 : PTY_u8;
2675 Operand &operand = GetOrCreateResOperand(parent, primType);
2676 MIRFunction *mirFunction = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(expr.GetPUIdx());
2677 CHECK_NULL_FATAL(mirFunction->GetFuncSymbol());
2678 SelectAddrof(operand, CreateStImmOperand(*mirFunction->GetFuncSymbol(), 0, 0));
2679 return operand;
2680 }
2681
2682 /* For an entire aggregate that can fit inside a single 8 byte register. */
GetDestTypeFromAggSize(uint32 bitSize) const2683 PrimType AArch64CGFunc::GetDestTypeFromAggSize(uint32 bitSize) const
2684 {
2685 PrimType primType;
2686 switch (bitSize) {
2687 case k8BitSize: {
2688 primType = PTY_u8;
2689 break;
2690 }
2691 case k16BitSize: {
2692 primType = PTY_u16;
2693 break;
2694 }
2695 case k32BitSize: {
2696 primType = PTY_u32;
2697 break;
2698 }
2699 case k64BitSize: {
2700 primType = PTY_u64;
2701 break;
2702 }
2703 default:
2704 CHECK_FATAL(false, "aggregate of unhandled size");
2705 }
2706 return primType;
2707 }
2708
SelectAddrofLabel(AddroflabelNode & expr,const BaseNode & parent)2709 Operand &AArch64CGFunc::SelectAddrofLabel(AddroflabelNode &expr, const BaseNode &parent)
2710 {
2711 /* adrp reg, label-id */
2712 uint32 instrSize = static_cast<uint32>(expr.SizeOfInstr());
2713 PrimType primType = (instrSize == k8ByteSize)
2714 ? PTY_u64
2715 : (instrSize == k4ByteSize) ? PTY_u32 : (instrSize == k2ByteSize) ? PTY_u16 : PTY_u8;
2716 Operand &dst = GetOrCreateResOperand(parent, primType);
2717 Operand &immOpnd = CreateImmOperand(expr.GetOffset(), k64BitSize, false);
2718 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_adrp_label, dst, immOpnd));
2719 return dst;
2720 }
2721
SelectIreadoff(const BaseNode & parent,IreadoffNode & ireadoff)2722 Operand *AArch64CGFunc::SelectIreadoff(const BaseNode &parent, IreadoffNode &ireadoff)
2723 {
2724 auto offset = ireadoff.GetOffset();
2725 auto primType = ireadoff.GetPrimType();
2726 auto bitSize = GetPrimTypeBitSize(primType);
2727 auto *baseAddr = ireadoff.Opnd(0);
2728 auto *result = &CreateRegisterOperandOfType(primType);
2729 auto *addrOpnd = HandleExpr(ireadoff, *baseAddr);
2730 ASSERT_NOT_NULL(addrOpnd);
2731 if (primType == PTY_agg && parent.GetOpCode() == OP_regassign) {
2732 auto &memOpnd = CreateMemOpnd(LoadIntoRegister(*addrOpnd, PTY_a64), offset, bitSize);
2733 auto mop = PickLdInsn(64, PTY_a64);
2734 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mop, *result, memOpnd));
2735 auto ®AssignNode = static_cast<const RegassignNode &>(parent);
2736 PregIdx pIdx = regAssignNode.GetRegIdx();
2737 CHECK_FATAL(IsSpecialPseudoRegister(pIdx), "SelectIreadfpoff of agg");
2738 (void)LmbcSmallAggForRet(parent, addrOpnd, offset, true);
2739 // result not used
2740 } else {
2741 auto &memOpnd = CreateMemOpnd(LoadIntoRegister(*addrOpnd, PTY_a64), offset, bitSize);
2742 auto mop = PickLdInsn(bitSize, primType);
2743 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mop, *result, memOpnd));
2744 }
2745 return result;
2746 }
2747
GenLmbcParamLoad(int32 offset,uint32 byteSize,RegType regType,PrimType primType,AArch64reg baseRegno)2748 RegOperand *AArch64CGFunc::GenLmbcParamLoad(int32 offset, uint32 byteSize, RegType regType, PrimType primType,
2749 AArch64reg baseRegno)
2750 {
2751 MemOperand *memOpnd = GenLmbcFpMemOperand(offset, byteSize, baseRegno);
2752 RegOperand *result = &GetOrCreateVirtualRegisterOperand(NewVReg(regType, byteSize));
2753 MOperator mOp = PickLdInsn(byteSize * kBitsPerByte, primType);
2754 Insn &load = GetInsnBuilder()->BuildInsn(mOp, *result, *memOpnd);
2755 GetCurBB()->AppendInsn(load);
2756 return result;
2757 }
2758
LmbcStructReturnLoad(int32 offset)2759 RegOperand *AArch64CGFunc::LmbcStructReturnLoad(int32 offset)
2760 {
2761 RegOperand *result = nullptr;
2762 MIRFunction &func = GetFunction();
2763 CHECK_FATAL(func.IsReturnStruct(), "LmbcStructReturnLoad: not struct return");
2764 MIRType *ty = func.GetReturnType();
2765 uint32 sz = GetBecommon().GetTypeSize(ty->GetTypeIndex());
2766 uint32 fpSize;
2767 uint32 numFpRegs = FloatParamRegRequired(static_cast<MIRStructType *>(ty), fpSize);
2768 if (numFpRegs > 0) {
2769 PrimType pType = (fpSize <= k4ByteSize) ? PTY_f32 : PTY_f64;
2770 for (int32 i = (numFpRegs - kOneRegister); i > 0; --i) {
2771 result = GenLmbcParamLoad(offset + (i * static_cast<int32>(fpSize)), fpSize, kRegTyFloat, pType);
2772 AArch64reg regNo = static_cast<AArch64reg>(V0 + static_cast<uint32>(i));
2773 RegOperand *reg = &GetOrCreatePhysicalRegisterOperand(regNo, fpSize * kBitsPerByte, kRegTyFloat);
2774 SelectCopy(*reg, pType, *result, pType);
2775 Insn &pseudo = GetInsnBuilder()->BuildInsn(MOP_pseudo_ret_float, *reg);
2776 GetCurBB()->AppendInsn(pseudo);
2777 }
2778 result = GenLmbcParamLoad(offset, fpSize, kRegTyFloat, pType);
2779 } else if (sz <= k4ByteSize) {
2780 result = GenLmbcParamLoad(offset, k4ByteSize, kRegTyInt, PTY_u32);
2781 } else if (sz <= k8ByteSize) {
2782 result = GenLmbcParamLoad(offset, k8ByteSize, kRegTyInt, PTY_i64);
2783 } else if (sz <= k16ByteSize) {
2784 result = GenLmbcParamLoad(offset + k8ByteSizeInt, k8ByteSize, kRegTyInt, PTY_i64);
2785 RegOperand *r1 = &GetOrCreatePhysicalRegisterOperand(R1, k8ByteSize * kBitsPerByte, kRegTyInt);
2786 SelectCopy(*r1, PTY_i64, *result, PTY_i64);
2787 Insn &pseudo = GetInsnBuilder()->BuildInsn(MOP_pseudo_ret_int, *r1);
2788 GetCurBB()->AppendInsn(pseudo);
2789 result = GenLmbcParamLoad(offset, k8ByteSize, kRegTyInt, PTY_i64);
2790 }
2791 return result;
2792 }
2793
SelectIreadfpoff(const BaseNode & parent,IreadFPoffNode & ireadoff)2794 Operand *AArch64CGFunc::SelectIreadfpoff(const BaseNode &parent, IreadFPoffNode &ireadoff)
2795 {
2796 uint32 offset = static_cast<uint32>(ireadoff.GetOffset());
2797 PrimType primType = ireadoff.GetPrimType();
2798 uint32 bytelen = GetPrimTypeSize(primType);
2799 uint32 bitlen = bytelen * kBitsPerByte;
2800 RegType regty = GetRegTyFromPrimTy(primType);
2801 RegOperand *result = nullptr;
2802 if (offset >= 0) {
2803 LmbcFormalParamInfo *info = GetLmbcFormalParamInfo(static_cast<uint32>(offset));
2804 DEBUG_ASSERT(info != nullptr, "nullptr check");
2805 if (info->GetPrimType() == PTY_agg) {
2806 if (info->IsOnStack()) {
2807 result = GenLmbcParamLoad(info->GetOnStackOffset(), GetPrimTypeSize(PTY_a64), kRegTyInt, PTY_a64);
2808 regno_t baseRegno = result->GetRegisterNumber();
2809 result = GenLmbcParamLoad(offset - static_cast<int32>(info->GetOffset()), bytelen, regty, primType,
2810 (AArch64reg)baseRegno);
2811 } else if (primType == PTY_agg) {
2812 CHECK_FATAL(parent.GetOpCode() == OP_regassign, "SelectIreadfpoff of agg");
2813 result = LmbcStructReturnLoad(offset);
2814 } else {
2815 result = GenLmbcParamLoad(offset, bytelen, regty, primType);
2816 }
2817 } else {
2818 CHECK_FATAL(primType == info->GetPrimType(), "Incorrect primtype");
2819 CHECK_FATAL(offset == info->GetOffset(), "Incorrect offset");
2820 if (info->GetRegNO() == 0 || !info->HasRegassign()) {
2821 result = GenLmbcParamLoad(offset, bytelen, regty, primType);
2822 } else {
2823 result = &GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(info->GetRegNO()), bitlen, regty);
2824 }
2825 }
2826 } else {
2827 if (primType == PTY_agg) {
2828 CHECK_FATAL(parent.GetOpCode() == OP_regassign, "SelectIreadfpoff of agg");
2829 result = LmbcStructReturnLoad(offset);
2830 } else {
2831 result = GenLmbcParamLoad(offset, bytelen, regty, primType);
2832 }
2833 }
2834 return result;
2835 }
2836
SelectIread(const BaseNode & parent,IreadNode & expr,int extraOffset,PrimType finalBitFieldDestType)2837 Operand *AArch64CGFunc::SelectIread(const BaseNode &parent, IreadNode &expr, int extraOffset,
2838 PrimType finalBitFieldDestType)
2839 {
2840 int32 offset = 0;
2841 MIRType *type = GlobalTables::GetTypeTable().GetTypeFromTyIdx(expr.GetTyIdx());
2842 MIRPtrType *pointerType = static_cast<MIRPtrType *>(type);
2843 DEBUG_ASSERT(pointerType != nullptr, "expect a pointer type at iread node");
2844 MIRType *pointedType = nullptr;
2845 bool isRefField = false;
2846 AArch64isa::MemoryOrdering memOrd = AArch64isa::kMoNone;
2847
2848 if (expr.GetFieldID() != 0) {
2849 MIRType *pointedTy = GlobalTables::GetTypeTable().GetTypeFromTyIdx(pointerType->GetPointedTyIdx());
2850 MIRStructType *structType = nullptr;
2851 if (pointedTy->GetKind() != kTypeJArray) {
2852 structType = static_cast<MIRStructType *>(pointedTy);
2853 } else {
2854 structType = static_cast<MIRJarrayType *>(pointedTy)->GetParentType();
2855 }
2856
2857 DEBUG_ASSERT(structType != nullptr, "SelectIread: non-zero fieldID for non-structure");
2858 pointedType = structType->GetFieldType(expr.GetFieldID());
2859 offset = GetBecommon().GetFieldOffset(*structType, expr.GetFieldID()).first;
2860 isRefField = GetBecommon().IsRefField(*structType, expr.GetFieldID());
2861 } else {
2862 pointedType = GetPointedToType(*pointerType);
2863 }
2864
2865 RegType regType = GetRegTyFromPrimTy(expr.GetPrimType());
2866 uint32 regSize = GetPrimTypeSize(expr.GetPrimType());
2867 if (expr.GetFieldID() == 0 && pointedType->GetPrimType() == PTY_agg) {
2868 /* Maple IR can passing small struct to be loaded into a single register. */
2869 if (regType == kRegTyFloat) {
2870 /* regsize is correct */
2871 } else {
2872 uint32 sz = GetBecommon().GetTypeSize(pointedType->GetTypeIndex().GetIdx());
2873 regSize = (sz <= k4ByteSize) ? k4ByteSize : k8ByteSize;
2874 }
2875 } else if (regSize < k4ByteSize) {
2876 regSize = k4ByteSize; /* 32-bit */
2877 }
2878 Operand *result = nullptr;
2879 constexpr int regSizeMax = 8;
2880 if (parent.GetOpCode() == OP_eval && regSize <= regSizeMax) {
2881 /* regSize << 3, that is regSize * 8, change bytes to bits */
2882 result = &GetZeroOpnd(regSize << 3);
2883 } else {
2884 result = &GetOrCreateResOperand(parent, expr.GetPrimType());
2885 }
2886
2887 PrimType destType = pointedType->GetPrimType();
2888
2889 uint32 bitSize = 0;
2890 if ((pointedType->GetKind() == kTypeStructIncomplete) || (pointedType->GetKind() == kTypeClassIncomplete) ||
2891 (pointedType->GetKind() == kTypeInterfaceIncomplete)) {
2892 bitSize = GetPrimTypeBitSize(expr.GetPrimType());
2893 maple::LogInfo::MapleLogger(kLlErr) << "Warning: objsize is zero! \n";
2894 } else {
2895 if (pointedType->IsStructType()) {
2896 MIRStructType *structType = static_cast<MIRStructType *>(pointedType);
2897 /* size << 3, that is size * 8, change bytes to bits */
2898 bitSize = std::min(structType->GetSize(), static_cast<size_t>(GetPointerSize())) << 3;
2899 } else {
2900 bitSize = GetPrimTypeBitSize(destType);
2901 }
2902 if (regType == kRegTyFloat) {
2903 destType = expr.GetPrimType();
2904 bitSize = GetPrimTypeBitSize(destType);
2905 } else if (destType == PTY_agg) {
2906 switch (bitSize) {
2907 case k8BitSize:
2908 destType = PTY_u8;
2909 break;
2910 case k16BitSize:
2911 destType = PTY_u16;
2912 break;
2913 case k32BitSize:
2914 destType = PTY_u32;
2915 break;
2916 case k64BitSize:
2917 destType = PTY_u64;
2918 break;
2919 default:
2920 destType = PTY_u64; // when eval agg . a way to round up
2921 DEBUG_ASSERT(bitSize == 0, " round up empty agg ");
2922 bitSize = k64BitSize;
2923 break;
2924 }
2925 }
2926 }
2927
2928 PrimType memType = (finalBitFieldDestType == kPtyInvalid ? destType : finalBitFieldDestType);
2929 MemOperand *memOpnd = CreateMemOpndOrNull(memType, expr, *expr.Opnd(0),
2930 static_cast<int64>(static_cast<int>(offset) + extraOffset), memOrd);
2931 if (aggParamReg != nullptr) {
2932 isAggParamInReg = false;
2933 return aggParamReg;
2934 }
2935 DEBUG_ASSERT(memOpnd != nullptr, "memOpnd should not be nullptr");
2936 if (isVolLoad && (memOpnd->GetAddrMode() == MemOperand::kAddrModeBOi)) {
2937 memOrd = AArch64isa::kMoAcquire;
2938 isVolLoad = false;
2939 }
2940
2941 memOpnd =
2942 memOpnd->IsOffsetMisaligned(bitSize) ? &ConstraintOffsetToSafeRegion(bitSize, *memOpnd, nullptr) : memOpnd;
2943 if (memOrd == AArch64isa::kMoNone) {
2944 MOperator mOp = 0;
2945 if (finalBitFieldDestType == kPtyInvalid) {
2946 mOp = PickLdInsn(bitSize, destType);
2947 } else {
2948 mOp = PickLdInsn(GetPrimTypeBitSize(finalBitFieldDestType), finalBitFieldDestType);
2949 }
2950 if ((memOpnd->GetMemVaryType() == kNotVary) && !IsOperandImmValid(mOp, memOpnd, 1)) {
2951 memOpnd = &SplitOffsetWithAddInstruction(*memOpnd, bitSize);
2952 }
2953 Insn &insn = GetInsnBuilder()->BuildInsn(mOp, *result, *memOpnd);
2954 if (parent.GetOpCode() == OP_eval && result->IsRegister() &&
2955 static_cast<RegOperand *>(result)->GetRegisterNumber() == RZR) {
2956 insn.SetComment("null-check");
2957 }
2958 GetCurBB()->AppendInsn(insn);
2959
2960 if (parent.op != OP_eval) {
2961 const InsnDesc *md = &AArch64CG::kMd[insn.GetMachineOpcode()];
2962 auto *prop = md->GetOpndDes(0);
2963 if ((prop->GetSize()) < insn.GetOperand(0).GetSize()) {
2964 switch (destType) {
2965 case PTY_i8:
2966 mOp = MOP_xsxtb64;
2967 break;
2968 case PTY_i16:
2969 mOp = MOP_xsxth64;
2970 break;
2971 case PTY_i32:
2972 mOp = MOP_xsxtw64;
2973 break;
2974 case PTY_u1:
2975 case PTY_u8:
2976 mOp = MOP_xuxtb32;
2977 break;
2978 case PTY_u16:
2979 mOp = MOP_xuxth32;
2980 break;
2981 case PTY_u32:
2982 mOp = MOP_xuxtw64;
2983 break;
2984 default:
2985 break;
2986 }
2987 if (destType == PTY_u1) {
2988 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wandrri12, insn.GetOperand(0),
2989 insn.GetOperand(0),
2990 CreateImmOperand(1, kMaxImmVal5Bits, false)));
2991 }
2992
2993 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, insn.GetOperand(0), insn.GetOperand(0)));
2994 }
2995 }
2996 } else {
2997 if ((memOpnd->GetMemVaryType() == kNotVary) && IsImmediateOffsetOutOfRange(*memOpnd, bitSize)) {
2998 memOpnd = &SplitOffsetWithAddInstruction(*memOpnd, bitSize);
2999 }
3000 AArch64CGFunc::SelectLoadAcquire(*result, destType, *memOpnd, destType, memOrd, false);
3001 }
3002 if (GetCurBB() && GetCurBB()->GetLastMachineInsn()) {
3003 GetCurBB()->GetLastMachineInsn()->MarkAsAccessRefField(isRefField);
3004 }
3005 return result;
3006 }
3007
SelectIntConst(const MIRIntConst & intConst,const BaseNode & parent)3008 Operand *AArch64CGFunc::SelectIntConst(const MIRIntConst &intConst, const BaseNode &parent)
3009 {
3010 auto primType = intConst.GetType().GetPrimType();
3011 if (kOpcodeInfo.IsCompare(parent.GetOpCode())) {
3012 primType = static_cast<const CompareNode &>(parent).GetOpndType();
3013 }
3014 return &CreateImmOperand(intConst.GetExtValue(), GetPrimTypeBitSize(primType), false);
3015 }
3016
SelectIntConst(const MIRIntConst & intConst)3017 Operand *AArch64CGFunc::SelectIntConst(const MIRIntConst &intConst)
3018 {
3019 return &CreateImmOperand(intConst.GetExtValue(), GetPrimTypeSize(intConst.GetType().GetPrimType()) * kBitsPerByte,
3020 false);
3021 }
3022
HandleFmovImm(PrimType stype,int64 val,MIRConst & mirConst,const BaseNode & parent)3023 Operand *AArch64CGFunc::HandleFmovImm(PrimType stype, int64 val, MIRConst &mirConst, const BaseNode &parent)
3024 {
3025 Operand *result;
3026 bool is64Bits = (GetPrimTypeBitSize(stype) == k64BitSize);
3027 uint64 canRepreset = is64Bits ? (val & 0xffffffffffff) : (val & 0x7ffff);
3028 uint32 val1 = is64Bits ? (val >> 61) & 0x3 : (val >> 29) & 0x3;
3029 uint32 val2 = is64Bits ? (val >> 54) & 0xff : (val >> 25) & 0x1f;
3030 bool isSame = is64Bits ? ((val2 == 0) || (val2 == 0xff)) : ((val2 == 0) || (val2 == 0x1f));
3031 canRepreset = (canRepreset == 0) && ((val1 & 0x1) ^ ((val1 & 0x2) >> 1)) && isSame;
3032 if (canRepreset) {
3033 uint64 temp1 = is64Bits ? (val >> 63) << 7 : (val >> 31) << 7;
3034 uint64 temp2 = is64Bits ? val >> 48 : val >> 19;
3035 int64 imm8 = (temp2 & 0x7f) | temp1;
3036 Operand *newOpnd0 = &CreateImmOperand(imm8, k8BitSize, true, kNotVary, true);
3037 result = &GetOrCreateResOperand(parent, stype);
3038 MOperator mopFmov = (is64Bits ? MOP_xdfmovri : MOP_wsfmovri);
3039 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopFmov, *result, *newOpnd0));
3040 } else {
3041 Operand *newOpnd0 = &CreateImmOperand(val, GetPrimTypeSize(stype) * kBitsPerByte, false);
3042 PrimType itype = (stype == PTY_f32) ? PTY_i32 : PTY_i64;
3043 RegOperand ®Opnd = LoadIntoRegister(*newOpnd0, itype);
3044
3045 result = &GetOrCreateResOperand(parent, stype);
3046 MOperator mopFmov = (is64Bits ? MOP_xvmovdr : MOP_xvmovsr);
3047 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopFmov, *result, regOpnd));
3048 }
3049 return result;
3050 }
3051
SelectFloatConst(MIRFloatConst & floatConst,const BaseNode & parent)3052 Operand *AArch64CGFunc::SelectFloatConst(MIRFloatConst &floatConst, const BaseNode &parent)
3053 {
3054 PrimType stype = floatConst.GetType().GetPrimType();
3055 int32 val = floatConst.GetIntValue();
3056 /* according to aarch64 encoding format, convert int to float expression */
3057 return HandleFmovImm(stype, val, floatConst, parent);
3058 }
3059
SelectDoubleConst(MIRDoubleConst & doubleConst,const BaseNode & parent)3060 Operand *AArch64CGFunc::SelectDoubleConst(MIRDoubleConst &doubleConst, const BaseNode &parent)
3061 {
3062 PrimType stype = doubleConst.GetType().GetPrimType();
3063 int64 val = doubleConst.GetIntValue();
3064 /* according to aarch64 encoding format, convert int to float expression */
3065 return HandleFmovImm(stype, val, doubleConst, parent);
3066 }
3067
3068 template <typename T>
SelectStrLiteral(T & c,AArch64CGFunc & cgFunc)3069 Operand *SelectStrLiteral(T &c, AArch64CGFunc &cgFunc)
3070 {
3071 std::string labelStr;
3072 if (c.GetKind() == kConstStrConst) {
3073 labelStr += ".LUstr_";
3074 } else if (c.GetKind() == kConstStr16Const) {
3075 labelStr += ".LUstr16_";
3076 } else {
3077 CHECK_FATAL(false, "Unsupported literal type");
3078 }
3079 labelStr += std::to_string(c.GetValue());
3080
3081 MIRSymbol *labelSym =
3082 GlobalTables::GetGsymTable().GetSymbolFromStrIdx(GlobalTables::GetStrTable().GetStrIdxFromName(labelStr));
3083 if (labelSym == nullptr) {
3084 labelSym = cgFunc.GetMirModule().GetMIRBuilder()->CreateGlobalDecl(labelStr, c.GetType());
3085 labelSym->SetStorageClass(kScFstatic);
3086 labelSym->SetSKind(kStConst);
3087 /* c may be local, we need a global node here */
3088 labelSym->SetKonst(cgFunc.NewMirConst(c));
3089 }
3090
3091 if (c.GetPrimType() == PTY_ptr) {
3092 StImmOperand &stOpnd = cgFunc.CreateStImmOperand(*labelSym, 0, 0);
3093 RegOperand &addrOpnd = cgFunc.CreateRegisterOperandOfType(PTY_a64);
3094 cgFunc.SelectAddrof(addrOpnd, stOpnd);
3095 return &addrOpnd;
3096 }
3097 CHECK_FATAL(false, "Unsupported const string type");
3098 return nullptr;
3099 }
3100
SelectStrConst(MIRStrConst & strConst)3101 Operand *AArch64CGFunc::SelectStrConst(MIRStrConst &strConst)
3102 {
3103 return SelectStrLiteral(strConst, *this);
3104 }
3105
SelectStr16Const(MIRStr16Const & str16Const)3106 Operand *AArch64CGFunc::SelectStr16Const(MIRStr16Const &str16Const)
3107 {
3108 return SelectStrLiteral(str16Const, *this);
3109 }
3110
AppendInstructionTo(Insn & i,CGFunc & f)3111 static inline void AppendInstructionTo(Insn &i, CGFunc &f)
3112 {
3113 f.GetCurBB()->AppendInsn(i);
3114 }
3115
3116 /*
3117 * Returns the number of leading 0-bits in x, starting at the most significant bit position.
3118 * If x is 0, the result is -1.
3119 */
GetHead0BitNum(int64 val)3120 static int32 GetHead0BitNum(int64 val)
3121 {
3122 uint32 bitNum = 0;
3123 for (; bitNum < k64BitSize; bitNum++) {
3124 if ((0x8000000000000000ULL >> static_cast<uint32>(bitNum)) & static_cast<uint64>(val)) {
3125 break;
3126 }
3127 }
3128 if (bitNum == k64BitSize) {
3129 return -1;
3130 }
3131 return bitNum;
3132 }
3133
3134 /*
3135 * Returns the number of trailing 0-bits in x, starting at the least significant bit position.
3136 * If x is 0, the result is -1.
3137 */
GetTail0BitNum(int64 val)3138 static int32 GetTail0BitNum(int64 val)
3139 {
3140 uint32 bitNum = 0;
3141 for (; bitNum < k64BitSize; bitNum++) {
3142 if ((static_cast<uint64>(1) << static_cast<uint32>(bitNum)) & static_cast<uint64>(val)) {
3143 break;
3144 }
3145 }
3146 if (bitNum == k64BitSize) {
3147 return -1;
3148 }
3149 return bitNum;
3150 }
3151
3152 /*
3153 * If the input integer is power of 2, return log2(input)
3154 * else return -1
3155 */
GetLog2(uint64 val)3156 static inline int32 GetLog2(uint64 val)
3157 {
3158 if (__builtin_popcountll(val) == 1) {
3159 return __builtin_ffsll(static_cast<int64>(val)) - 1;
3160 }
3161 return -1;
3162 }
3163
PickJmpInsn(Opcode brOp,Opcode cmpOp,bool isFloat,bool isSigned) const3164 MOperator AArch64CGFunc::PickJmpInsn(Opcode brOp, Opcode cmpOp, bool isFloat, bool isSigned) const
3165 {
3166 switch (cmpOp) {
3167 case OP_ne:
3168 return (brOp == OP_brtrue) ? MOP_bne : MOP_beq;
3169 case OP_eq:
3170 return (brOp == OP_brtrue) ? MOP_beq : MOP_bne;
3171 case OP_lt:
3172 return (brOp == OP_brtrue) ? (isSigned ? MOP_blt : MOP_blo)
3173 : (isFloat ? MOP_bpl : (isSigned ? MOP_bge : MOP_bhs));
3174 case OP_le:
3175 return (brOp == OP_brtrue) ? (isSigned ? MOP_ble : MOP_bls)
3176 : (isFloat ? MOP_bhi : (isSigned ? MOP_bgt : MOP_bhi));
3177 case OP_gt:
3178 return (brOp == OP_brtrue) ? (isFloat ? MOP_bgt : (isSigned ? MOP_bgt : MOP_bhi))
3179 : (isSigned ? MOP_ble : MOP_bls);
3180 case OP_ge:
3181 return (brOp == OP_brtrue) ? (isFloat ? MOP_bpl : (isSigned ? MOP_bge : MOP_bhs))
3182 : (isSigned ? MOP_blt : MOP_blo);
3183 default:
3184 CHECK_FATAL(false, "PickJmpInsn error");
3185 }
3186 }
3187
GenerateCompareWithZeroInstruction(Opcode jmpOp,Opcode cmpOp,bool is64Bits,PrimType primType,LabelOperand & targetOpnd,Operand & opnd0)3188 bool AArch64CGFunc::GenerateCompareWithZeroInstruction(Opcode jmpOp, Opcode cmpOp, bool is64Bits, PrimType primType,
3189 LabelOperand &targetOpnd, Operand &opnd0)
3190 {
3191 bool finish = true;
3192 MOperator mOpCode = MOP_undef;
3193 switch (cmpOp) {
3194 case OP_ne: {
3195 if (jmpOp == OP_brtrue) {
3196 mOpCode = is64Bits ? MOP_xcbnz : MOP_wcbnz;
3197 } else {
3198 mOpCode = is64Bits ? MOP_xcbz : MOP_wcbz;
3199 }
3200 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, opnd0, targetOpnd));
3201 break;
3202 }
3203 case OP_eq: {
3204 if (jmpOp == OP_brtrue) {
3205 mOpCode = is64Bits ? MOP_xcbz : MOP_wcbz;
3206 } else {
3207 mOpCode = is64Bits ? MOP_xcbnz : MOP_wcbnz;
3208 }
3209 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, opnd0, targetOpnd));
3210 break;
3211 }
3212 /*
3213 * TBZ/TBNZ instruction have a range of +/-32KB, need to check if the jump target is reachable in a later
3214 * phase. If the branch target is not reachable, then we change tbz/tbnz into combination of ubfx and
3215 * cbz/cbnz, which will clobber one extra register. With LSRA under O2, we can use of the reserved registers
3216 * for that purpose.
3217 */
3218 case OP_lt: {
3219 if (primType == PTY_u64 || primType == PTY_u32) {
3220 return false;
3221 }
3222 ImmOperand &signBit =
3223 CreateImmOperand(is64Bits ? kHighestBitOf64Bits : kHighestBitOf32Bits, k8BitSize, false);
3224 if (jmpOp == OP_brtrue) {
3225 mOpCode = is64Bits ? MOP_xtbnz : MOP_wtbnz;
3226 } else {
3227 mOpCode = is64Bits ? MOP_xtbz : MOP_wtbz;
3228 }
3229 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, opnd0, signBit, targetOpnd));
3230 break;
3231 }
3232 case OP_ge: {
3233 if (primType == PTY_u64 || primType == PTY_u32) {
3234 return false;
3235 }
3236 ImmOperand &signBit =
3237 CreateImmOperand(is64Bits ? kHighestBitOf64Bits : kHighestBitOf32Bits, k8BitSize, false);
3238 if (jmpOp == OP_brtrue) {
3239 mOpCode = is64Bits ? MOP_xtbz : MOP_wtbz;
3240 } else {
3241 mOpCode = is64Bits ? MOP_xtbnz : MOP_wtbnz;
3242 }
3243 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, opnd0, signBit, targetOpnd));
3244 break;
3245 }
3246 default:
3247 finish = false;
3248 break;
3249 }
3250 return finish;
3251 }
3252
SelectIgoto(Operand * opnd0)3253 void AArch64CGFunc::SelectIgoto(Operand *opnd0)
3254 {
3255 Operand *srcOpnd = opnd0;
3256 if (opnd0->GetKind() == Operand::kOpdMem) {
3257 Operand *dst = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
3258 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xldr, *dst, *opnd0));
3259 srcOpnd = dst;
3260 }
3261 GetCurBB()->SetKind(BB::kBBIgoto);
3262 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xbr, *srcOpnd));
3263 }
3264
SelectCondGoto(LabelOperand & targetOpnd,Opcode jmpOp,Opcode cmpOp,Operand & origOpnd0,Operand & origOpnd1,PrimType primType,bool signedCond)3265 void AArch64CGFunc::SelectCondGoto(LabelOperand &targetOpnd, Opcode jmpOp, Opcode cmpOp, Operand &origOpnd0,
3266 Operand &origOpnd1, PrimType primType, bool signedCond)
3267 {
3268 Operand *opnd0 = &origOpnd0;
3269 Operand *opnd1 = &origOpnd1;
3270 opnd0 = &LoadIntoRegister(origOpnd0, primType);
3271
3272 bool is64Bits = GetPrimTypeBitSize(primType) == k64BitSize;
3273 bool isFloat = IsPrimitiveFloat(primType);
3274 Operand &rflag = GetOrCreateRflag();
3275 if (isFloat) {
3276 opnd1 = &LoadIntoRegister(origOpnd1, primType);
3277 MOperator mOp =
3278 is64Bits ? MOP_dcmperr : ((GetPrimTypeBitSize(primType) == k32BitSize) ? MOP_scmperr : MOP_hcmperr);
3279 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, rflag, *opnd0, *opnd1));
3280 } else {
3281 bool isImm = ((origOpnd1.GetKind() == Operand::kOpdImmediate) || (origOpnd1.GetKind() == Operand::kOpdOffset));
3282 if ((origOpnd1.GetKind() != Operand::kOpdRegister) && !isImm) {
3283 opnd1 = &SelectCopy(origOpnd1, primType, primType);
3284 }
3285 MOperator mOp = is64Bits ? MOP_xcmprr : MOP_wcmprr;
3286
3287 if (isImm) {
3288 if (static_cast<ImmOperand *>(opnd1)->IsZero() &&
3289 (Globals::GetInstance()->GetOptimLevel() > CGOptions::kLevel0)) {
3290 bool finish = GenerateCompareWithZeroInstruction(jmpOp, cmpOp, is64Bits, primType, targetOpnd, *opnd0);
3291 if (finish) {
3292 return;
3293 }
3294 }
3295
3296 /*
3297 * aarch64 assembly takes up to 24-bits immediate, generating
3298 * either cmp or cmp with shift 12 encoding
3299 */
3300 ImmOperand *immOpnd = static_cast<ImmOperand *>(opnd1);
3301 if (immOpnd->IsInBitSize(kMaxImmVal12Bits, 0) || immOpnd->IsInBitSize(kMaxImmVal12Bits, kMaxImmVal12Bits)) {
3302 mOp = is64Bits ? MOP_xcmpri : MOP_wcmpri;
3303 } else {
3304 opnd1 = &SelectCopy(*opnd1, primType, primType);
3305 }
3306 }
3307 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, rflag, *opnd0, *opnd1));
3308 }
3309
3310 bool isSigned = IsPrimitiveInteger(primType) ? IsSignedInteger(primType) : (signedCond ? true : false);
3311 MOperator jmpOperator = PickJmpInsn(jmpOp, cmpOp, isFloat, isSigned);
3312 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(jmpOperator, rflag, targetOpnd));
3313 }
3314
3315 /*
3316 * brtrue @label0 (ge u8 i32 (
3317 * cmp i32 i64 (dread i64 %Reg2_J, dread i64 %Reg4_J),
3318 * constval i32 0))
3319 * ===>
3320 * cmp r1, r2
3321 * bge Cond, label0
3322 */
SelectCondSpecialCase1(CondGotoNode & stmt,BaseNode & expr)3323 void AArch64CGFunc::SelectCondSpecialCase1(CondGotoNode &stmt, BaseNode &expr)
3324 {
3325 DEBUG_ASSERT(expr.GetOpCode() == OP_cmp, "unexpect opcode");
3326 Operand *opnd0 = HandleExpr(expr, *expr.Opnd(0));
3327 Operand *opnd1 = HandleExpr(expr, *expr.Opnd(1));
3328 CompareNode *node = static_cast<CompareNode *>(&expr);
3329 bool isFloat = IsPrimitiveFloat(node->GetOpndType());
3330 opnd0 = &LoadIntoRegister(*opnd0, node->GetOpndType());
3331 /*
3332 * most of FP constants are passed as MemOperand
3333 * except 0.0 which is passed as kOpdFPImmediate
3334 */
3335 Operand::OperandType opnd1Type = opnd1->GetKind();
3336 if ((opnd1Type != Operand::kOpdImmediate) && (opnd1Type != Operand::kOpdFPImmediate) &&
3337 (opnd1Type != Operand::kOpdOffset)) {
3338 opnd1 = &LoadIntoRegister(*opnd1, node->GetOpndType());
3339 }
3340 SelectAArch64Cmp(*opnd0, *opnd1, !isFloat, GetPrimTypeBitSize(node->GetOpndType()));
3341 /* handle condgoto now. */
3342 LabelIdx labelIdx = stmt.GetOffset();
3343 BaseNode *condNode = stmt.Opnd(0);
3344 LabelOperand &targetOpnd = GetOrCreateLabelOperand(labelIdx);
3345 Opcode cmpOp = condNode->GetOpCode();
3346 PrimType pType = static_cast<CompareNode *>(condNode)->GetOpndType();
3347 isFloat = IsPrimitiveFloat(pType);
3348 Operand &rflag = GetOrCreateRflag();
3349 bool isSigned =
3350 IsPrimitiveInteger(pType) ? IsSignedInteger(pType) : (IsSignedInteger(condNode->GetPrimType()) ? true : false);
3351 MOperator jmpOp = PickJmpInsn(stmt.GetOpCode(), cmpOp, isFloat, isSigned);
3352 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(jmpOp, rflag, targetOpnd));
3353 }
3354
3355 /*
3356 * Special case:
3357 * brfalse(ge (cmpg (op0, op1), 0) ==>
3358 * fcmp op1, op2
3359 * blo
3360 */
SelectCondSpecialCase2(const CondGotoNode & stmt,BaseNode & expr)3361 void AArch64CGFunc::SelectCondSpecialCase2(const CondGotoNode &stmt, BaseNode &expr)
3362 {
3363 auto &cmpNode = static_cast<CompareNode &>(expr);
3364 Operand *opnd0 = HandleExpr(cmpNode, *cmpNode.Opnd(0));
3365 Operand *opnd1 = HandleExpr(cmpNode, *cmpNode.Opnd(1));
3366 PrimType operandType = cmpNode.GetOpndType();
3367 opnd0 = opnd0->IsRegister() ? static_cast<RegOperand *>(opnd0) : &SelectCopy(*opnd0, operandType, operandType);
3368 Operand::OperandType opnd1Type = opnd1->GetKind();
3369 if ((opnd1Type != Operand::kOpdImmediate) && (opnd1Type != Operand::kOpdFPImmediate) &&
3370 (opnd1Type != Operand::kOpdOffset)) {
3371 opnd1 = opnd1->IsRegister() ? static_cast<RegOperand *>(opnd1) : &SelectCopy(*opnd1, operandType, operandType);
3372 }
3373 #ifdef DEBUG
3374 bool isFloat = IsPrimitiveFloat(operandType);
3375 if (!isFloat) {
3376 DEBUG_ASSERT(false, "incorrect operand types");
3377 }
3378 #endif
3379 SelectTargetFPCmpQuiet(*opnd0, *opnd1, GetPrimTypeBitSize(operandType));
3380 Operand &rFlag = GetOrCreateRflag();
3381 LabelIdx tempLabelIdx = stmt.GetOffset();
3382 LabelOperand &targetOpnd = GetOrCreateLabelOperand(tempLabelIdx);
3383 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_blo, rFlag, targetOpnd));
3384 }
3385
SelectCondGoto(CondGotoNode & stmt,Operand & opnd0,Operand & opnd1)3386 void AArch64CGFunc::SelectCondGoto(CondGotoNode &stmt, Operand &opnd0, Operand &opnd1)
3387 {
3388 /*
3389 * handle brfalse/brtrue op, opnd0 can be a compare node or non-compare node
3390 * such as a dread for example
3391 */
3392 LabelIdx labelIdx = stmt.GetOffset();
3393 BaseNode *condNode = stmt.Opnd(0);
3394 LabelOperand &targetOpnd = GetOrCreateLabelOperand(labelIdx);
3395 Opcode cmpOp;
3396
3397 if (opnd0.IsRegister() && (static_cast<RegOperand *>(&opnd0)->GetValidBitsNum() == 1) &&
3398 (condNode->GetOpCode() == OP_lior)) {
3399 ImmOperand &condBit = CreateImmOperand(0, k8BitSize, false);
3400 if (stmt.GetOpCode() == OP_brtrue) {
3401 GetCurBB()->AppendInsn(
3402 GetInsnBuilder()->BuildInsn(MOP_wtbnz, static_cast<RegOperand &>(opnd0), condBit, targetOpnd));
3403 } else {
3404 GetCurBB()->AppendInsn(
3405 GetInsnBuilder()->BuildInsn(MOP_wtbz, static_cast<RegOperand &>(opnd0), condBit, targetOpnd));
3406 }
3407 return;
3408 }
3409
3410 PrimType pType;
3411 if (kOpcodeInfo.IsCompare(condNode->GetOpCode())) {
3412 cmpOp = condNode->GetOpCode();
3413 pType = static_cast<CompareNode *>(condNode)->GetOpndType();
3414 } else {
3415 /* not a compare node; dread for example, take its pType */
3416 cmpOp = OP_ne;
3417 pType = condNode->GetPrimType();
3418 }
3419 bool signedCond = IsSignedInteger(pType) || IsPrimitiveFloat(pType);
3420 SelectCondGoto(targetOpnd, stmt.GetOpCode(), cmpOp, opnd0, opnd1, pType, signedCond);
3421 }
3422
SelectGoto(GotoNode & stmt)3423 void AArch64CGFunc::SelectGoto(GotoNode &stmt)
3424 {
3425 Operand &targetOpnd = GetOrCreateLabelOperand(stmt.GetOffset());
3426 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xuncond, targetOpnd));
3427 GetCurBB()->SetKind(BB::kBBGoto);
3428 }
3429
SelectAdd(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)3430 Operand *AArch64CGFunc::SelectAdd(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
3431 {
3432 PrimType dtype = node.GetPrimType();
3433 bool isSigned = IsSignedInteger(dtype);
3434 uint32 dsize = GetPrimTypeBitSize(dtype);
3435 bool is64Bits = (dsize == k64BitSize);
3436 bool isFloat = IsPrimitiveFloat(dtype);
3437 RegOperand *resOpnd = nullptr;
3438 if (!IsPrimitiveVector(dtype)) {
3439 /* promoted type */
3440 PrimType primType =
3441 isFloat ? dtype : ((is64Bits ? (isSigned ? PTY_i64 : PTY_u64) : (isSigned ? PTY_i32 : PTY_u32)));
3442 if (parent.GetOpCode() == OP_regassign) {
3443 auto ®AssignNode = static_cast<const RegassignNode &>(parent);
3444 PregIdx pregIdx = regAssignNode.GetRegIdx();
3445 if (IsSpecialPseudoRegister(pregIdx)) {
3446 resOpnd = &GetOrCreateSpecialRegisterOperand(-pregIdx, dtype);
3447 } else {
3448 resOpnd = &GetOrCreateVirtualRegisterOperand(GetVirtualRegNOFromPseudoRegIdx(pregIdx));
3449 }
3450 } else {
3451 resOpnd = &CreateRegisterOperandOfType(primType);
3452 }
3453 SelectAdd(*resOpnd, opnd0, opnd1, primType);
3454 } else {
3455 /* vector operands */
3456 resOpnd =
3457 SelectVectorBinOp(dtype, &opnd0, node.Opnd(0)->GetPrimType(), &opnd1, node.Opnd(1)->GetPrimType(), OP_add);
3458 }
3459 return resOpnd;
3460 }
3461
SelectAdd(Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType)3462 void AArch64CGFunc::SelectAdd(Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType)
3463 {
3464 Operand::OperandType opnd0Type = opnd0.GetKind();
3465 Operand::OperandType opnd1Type = opnd1.GetKind();
3466 uint32 dsize = GetPrimTypeBitSize(primType);
3467 bool is64Bits = (dsize == k64BitSize);
3468 if (opnd0Type != Operand::kOpdRegister) {
3469 /* add #imm, #imm */
3470 if (opnd1Type != Operand::kOpdRegister) {
3471 SelectAdd(resOpnd, SelectCopy(opnd0, primType, primType), opnd1, primType);
3472 return;
3473 }
3474 /* add #imm, reg */
3475 SelectAdd(resOpnd, opnd1, opnd0, primType); /* commutative */
3476 return;
3477 }
3478 /* add reg, reg */
3479 if (opnd1Type == Operand::kOpdRegister) {
3480 DEBUG_ASSERT(IsPrimitiveFloat(primType) || IsPrimitiveInteger(primType), "NYI add");
3481 MOperator mOp =
3482 IsPrimitiveFloat(primType) ? (is64Bits ? MOP_dadd : MOP_sadd) : (is64Bits ? MOP_xaddrrr : MOP_waddrrr);
3483 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opnd0, opnd1));
3484 return;
3485 } else if (opnd1Type == Operand::kOpdStImmediate) {
3486 CHECK_FATAL(is64Bits, "baseReg of mem in aarch64 must be 64bit size");
3487 /* add reg, reg, #:lo12:sym+offset */
3488 StImmOperand &stImmOpnd = static_cast<StImmOperand &>(opnd1);
3489 Insn &newInsn = GetInsnBuilder()->BuildInsn(MOP_xadrpl12, resOpnd, opnd0, stImmOpnd);
3490 GetCurBB()->AppendInsn(newInsn);
3491 return;
3492 } else if (!((opnd1Type == Operand::kOpdImmediate) || (opnd1Type == Operand::kOpdOffset))) {
3493 /* add reg, otheregType */
3494 SelectAdd(resOpnd, opnd0, SelectCopy(opnd1, primType, primType), primType);
3495 return;
3496 } else {
3497 /* add reg, #imm */
3498 ImmOperand *immOpnd = static_cast<ImmOperand *>(&opnd1);
3499 if (immOpnd->IsNegative()) {
3500 immOpnd->Negate();
3501 SelectSub(resOpnd, opnd0, *immOpnd, primType);
3502 return;
3503 }
3504 if (immOpnd->IsInBitSize(kMaxImmVal24Bits, 0)) {
3505 /*
3506 * ADD Wd|WSP, Wn|WSP, #imm{, shift} ; 32-bit general registers
3507 * ADD Xd|SP, Xn|SP, #imm{, shift} ; 64-bit general registers
3508 * imm : 0 ~ 4095, shift: none, LSL #0, or LSL #12
3509 * aarch64 assembly takes up to 24-bits, if the lower 12 bits is all 0
3510 */
3511 MOperator mOpCode = MOP_undef;
3512 Operand *newOpnd0 = &opnd0;
3513 if (!(immOpnd->IsInBitSize(kMaxImmVal12Bits, 0) ||
3514 immOpnd->IsInBitSize(kMaxImmVal12Bits, kMaxImmVal12Bits))) {
3515 /* process higher 12 bits */
3516 ImmOperand &immOpnd2 =
3517 CreateImmOperand(static_cast<int64>(static_cast<uint64>(immOpnd->GetValue()) >> kMaxImmVal12Bits),
3518 immOpnd->GetSize(), immOpnd->IsSignedValue());
3519 mOpCode = is64Bits ? MOP_xaddrri24 : MOP_waddrri24;
3520 Operand *tmpRes = IsAfterRegAlloc() ? &resOpnd : &CreateRegisterOperandOfType(primType);
3521 BitShiftOperand &shiftopnd = CreateBitShiftOperand(BitShiftOperand::kLSL, kShiftAmount12, k64BitSize);
3522 Insn &newInsn = GetInsnBuilder()->BuildInsn(mOpCode, *tmpRes, opnd0, immOpnd2, shiftopnd);
3523 GetCurBB()->AppendInsn(newInsn);
3524 immOpnd->ModuloByPow2(static_cast<int32>(kMaxImmVal12Bits));
3525 newOpnd0 = tmpRes;
3526 }
3527 /* process lower 12 bits */
3528 mOpCode = is64Bits ? MOP_xaddrri12 : MOP_waddrri12;
3529 Insn &newInsn = GetInsnBuilder()->BuildInsn(mOpCode, resOpnd, *newOpnd0, *immOpnd);
3530 GetCurBB()->AppendInsn(newInsn);
3531 return;
3532 }
3533 /* load into register */
3534 int64 immVal = immOpnd->GetValue();
3535 int32 tail0bitNum = GetTail0BitNum(immVal);
3536 int32 head0bitNum = GetHead0BitNum(immVal);
3537 const int32 bitNum = (k64BitSizeInt - head0bitNum) - tail0bitNum;
3538 RegOperand ®Opnd = CreateRegisterOperandOfType(primType);
3539 if (isAfterRegAlloc) {
3540 RegType regty = GetRegTyFromPrimTy(primType);
3541 uint32 bytelen = GetPrimTypeSize(primType);
3542 regOpnd = GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(R16), bytelen, regty);
3543 }
3544 regno_t regNO0 = static_cast<RegOperand &>(opnd0).GetRegisterNumber();
3545 /* addrrrs do not support sp */
3546 if (bitNum <= k16ValidBit && regNO0 != RSP) {
3547 int64 newImm = (static_cast<uint64>(immVal) >> static_cast<uint32>(tail0bitNum)) & 0xFFFF;
3548 ImmOperand &immOpnd1 = CreateImmOperand(newImm, k16BitSize, false);
3549 SelectCopyImm(regOpnd, immOpnd1, primType);
3550 uint32 mopBadd = is64Bits ? MOP_xaddrrrs : MOP_waddrrrs;
3551 int32 bitLen = is64Bits ? kBitLenOfShift64Bits : kBitLenOfShift32Bits;
3552 BitShiftOperand &bitShiftOpnd =
3553 CreateBitShiftOperand(BitShiftOperand::kLSL, static_cast<uint32>(tail0bitNum), bitLen);
3554 Insn &newInsn = GetInsnBuilder()->BuildInsn(mopBadd, resOpnd, opnd0, regOpnd, bitShiftOpnd);
3555 GetCurBB()->AppendInsn(newInsn);
3556 return;
3557 }
3558
3559 SelectCopyImm(regOpnd, *immOpnd, primType);
3560 MOperator mOpCode = is64Bits ? MOP_xaddrrr : MOP_waddrrr;
3561 Insn &newInsn = GetInsnBuilder()->BuildInsn(mOpCode, resOpnd, opnd0, regOpnd);
3562 GetCurBB()->AppendInsn(newInsn);
3563 }
3564 }
3565
SelectMadd(BinaryNode & node,Operand & opndM0,Operand & opndM1,Operand & opnd1,const BaseNode & parent)3566 Operand *AArch64CGFunc::SelectMadd(BinaryNode &node, Operand &opndM0, Operand &opndM1, Operand &opnd1,
3567 const BaseNode &parent)
3568 {
3569 PrimType dtype = node.GetPrimType();
3570 bool isSigned = IsSignedInteger(dtype);
3571 uint32 dsize = GetPrimTypeBitSize(dtype);
3572 bool is64Bits = (dsize == k64BitSize);
3573 /* promoted type */
3574 PrimType primType = is64Bits ? (isSigned ? PTY_i64 : PTY_u64) : (isSigned ? PTY_i32 : PTY_u32);
3575 RegOperand &resOpnd = GetOrCreateResOperand(parent, primType);
3576 SelectMadd(resOpnd, opndM0, opndM1, opnd1, primType);
3577 return &resOpnd;
3578 }
3579
SelectMadd(Operand & resOpnd,Operand & opndM0,Operand & opndM1,Operand & opnd1,PrimType primType)3580 void AArch64CGFunc::SelectMadd(Operand &resOpnd, Operand &opndM0, Operand &opndM1, Operand &opnd1, PrimType primType)
3581 {
3582 Operand::OperandType opndM0Type = opndM0.GetKind();
3583 Operand::OperandType opndM1Type = opndM1.GetKind();
3584 Operand::OperandType opnd1Type = opnd1.GetKind();
3585 uint32 dsize = GetPrimTypeBitSize(primType);
3586 bool is64Bits = (dsize == k64BitSize);
3587
3588 if (opndM0Type != Operand::kOpdRegister) {
3589 SelectMadd(resOpnd, SelectCopy(opndM0, primType, primType), opndM1, opnd1, primType);
3590 return;
3591 } else if (opndM1Type != Operand::kOpdRegister) {
3592 SelectMadd(resOpnd, opndM0, SelectCopy(opndM1, primType, primType), opnd1, primType);
3593 return;
3594 } else if (opnd1Type != Operand::kOpdRegister) {
3595 SelectMadd(resOpnd, opndM0, opndM1, SelectCopy(opnd1, primType, primType), primType);
3596 return;
3597 }
3598
3599 DEBUG_ASSERT(IsPrimitiveInteger(primType), "NYI MAdd");
3600 MOperator mOp = is64Bits ? MOP_xmaddrrrr : MOP_wmaddrrrr;
3601 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opndM0, opndM1, opnd1));
3602 }
3603
SelectCGArrayElemAdd(BinaryNode & node,const BaseNode & parent)3604 Operand &AArch64CGFunc::SelectCGArrayElemAdd(BinaryNode &node, const BaseNode &parent)
3605 {
3606 BaseNode *opnd0 = node.Opnd(0);
3607 BaseNode *opnd1 = node.Opnd(1);
3608 DEBUG_ASSERT(opnd1->GetOpCode() == OP_constval, "Internal error, opnd1->op should be OP_constval.");
3609
3610 switch (opnd0->op) {
3611 case OP_regread: {
3612 RegreadNode *regreadNode = static_cast<RegreadNode *>(opnd0);
3613 return *SelectRegread(*regreadNode);
3614 }
3615 case OP_addrof: {
3616 AddrofNode *addrofNode = static_cast<AddrofNode *>(opnd0);
3617 CHECK_NULL_FATAL(mirModule.CurFunction());
3618 CHECK_NULL_FATAL(mirModule.CurFunction()->GetLocalOrGlobalSymbol(addrofNode->GetStIdx()));
3619 MIRSymbol &symbol = *mirModule.CurFunction()->GetLocalOrGlobalSymbol(addrofNode->GetStIdx());
3620 DEBUG_ASSERT(addrofNode->GetFieldID() == 0, "For debug SelectCGArrayElemAdd.");
3621
3622 Operand &result = GetOrCreateResOperand(parent, PTY_a64);
3623
3624 /* OP_constval */
3625 ConstvalNode *constvalNode = static_cast<ConstvalNode *>(opnd1);
3626 MIRConst *mirConst = constvalNode->GetConstVal();
3627 MIRIntConst *mirIntConst = static_cast<MIRIntConst *>(mirConst);
3628 SelectAddrof(result, CreateStImmOperand(symbol, mirIntConst->GetExtValue(), 0));
3629
3630 return result;
3631 }
3632 default:
3633 CHECK_FATAL(0, "Internal error, cannot handle opnd0.");
3634 }
3635 }
3636
SelectSub(Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType)3637 void AArch64CGFunc::SelectSub(Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType)
3638 {
3639 Operand::OperandType opnd1Type = opnd1.GetKind();
3640 uint32 dsize = GetPrimTypeBitSize(primType);
3641 bool is64Bits = (dsize == k64BitSize);
3642 bool isFloat = IsPrimitiveFloat(primType);
3643 Operand *opnd0Bak = &LoadIntoRegister(opnd0, primType);
3644 if (opnd1Type == Operand::kOpdRegister) {
3645 MOperator mOp = isFloat ? (is64Bits ? MOP_dsub : MOP_ssub) : (is64Bits ? MOP_xsubrrr : MOP_wsubrrr);
3646 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, *opnd0Bak, opnd1));
3647 return;
3648 }
3649
3650 if ((opnd1Type != Operand::kOpdImmediate) && (opnd1Type != Operand::kOpdOffset)) {
3651 SelectSub(resOpnd, *opnd0Bak, SelectCopy(opnd1, primType, primType), primType);
3652 return;
3653 }
3654
3655 ImmOperand *immOpnd = static_cast<ImmOperand *>(&opnd1);
3656 if (immOpnd->IsNegative()) {
3657 immOpnd->Negate();
3658 SelectAdd(resOpnd, *opnd0Bak, *immOpnd, primType);
3659 return;
3660 }
3661
3662 int64 higher12BitVal = static_cast<int64>(static_cast<uint64>(immOpnd->GetValue()) >> kMaxImmVal12Bits);
3663 if (immOpnd->IsInBitSize(kMaxImmVal24Bits, 0) && higher12BitVal + 1 <= kMaxPimm8) {
3664 /*
3665 * SUB Wd|WSP, Wn|WSP, #imm{, shift} ; 32-bit general registers
3666 * SUB Xd|SP, Xn|SP, #imm{, shift} ; 64-bit general registers
3667 * imm : 0 ~ 4095, shift: none, LSL #0, or LSL #12
3668 * aarch64 assembly takes up to 24-bits, if the lower 12 bits is all 0
3669 * large offset is treated as sub (higher 12 bits + 4096) + add
3670 * it gives opportunities for combining add + ldr due to the characteristics of aarch64's load/store
3671 */
3672 MOperator mOpCode = MOP_undef;
3673 bool isSplitSub = false;
3674 if (!(immOpnd->IsInBitSize(kMaxImmVal12Bits, 0) || immOpnd->IsInBitSize(kMaxImmVal12Bits, kMaxImmVal12Bits))) {
3675 isSplitSub = true;
3676 /* process higher 12 bits */
3677 ImmOperand &immOpnd2 = CreateImmOperand(higher12BitVal + 1, immOpnd->GetSize(), immOpnd->IsSignedValue());
3678
3679 mOpCode = is64Bits ? MOP_xsubrri24 : MOP_wsubrri24;
3680 BitShiftOperand &shiftopnd = CreateBitShiftOperand(BitShiftOperand::kLSL, kShiftAmount12, k64BitSize);
3681 Insn &newInsn = GetInsnBuilder()->BuildInsn(mOpCode, resOpnd, *opnd0Bak, immOpnd2, shiftopnd);
3682 GetCurBB()->AppendInsn(newInsn);
3683 immOpnd->ModuloByPow2(static_cast<int64>(kMaxImmVal12Bits));
3684 immOpnd->SetValue(static_cast<int64>(kMax12UnsignedImm) - immOpnd->GetValue());
3685 opnd0Bak = &resOpnd;
3686 }
3687 /* process lower 12 bits */
3688 mOpCode = isSplitSub ? (is64Bits ? MOP_xaddrri12 : MOP_waddrri12) : (is64Bits ? MOP_xsubrri12 : MOP_wsubrri12);
3689 Insn &newInsn = GetInsnBuilder()->BuildInsn(mOpCode, resOpnd, *opnd0Bak, *immOpnd);
3690 GetCurBB()->AppendInsn(newInsn);
3691 return;
3692 }
3693
3694 /* load into register */
3695 int64 immVal = immOpnd->GetValue();
3696 int32 tail0bitNum = GetTail0BitNum(immVal);
3697 int32 head0bitNum = GetHead0BitNum(immVal);
3698 const int32 bitNum = (k64BitSizeInt - head0bitNum) - tail0bitNum;
3699 RegOperand ®Opnd = CreateRegisterOperandOfType(primType);
3700 if (isAfterRegAlloc) {
3701 RegType regty = GetRegTyFromPrimTy(primType);
3702 uint32 bytelen = GetPrimTypeSize(primType);
3703 regOpnd = GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(R16), bytelen, regty);
3704 }
3705
3706 if (bitNum <= k16ValidBit) {
3707 int64 newImm = (static_cast<uint64>(immVal) >> static_cast<uint32>(tail0bitNum)) & 0xFFFF;
3708 ImmOperand &immOpnd1 = CreateImmOperand(newImm, k16BitSize, false);
3709 SelectCopyImm(regOpnd, immOpnd1, primType);
3710 uint32 mopBsub = is64Bits ? MOP_xsubrrrs : MOP_wsubrrrs;
3711 int32 bitLen = is64Bits ? kBitLenOfShift64Bits : kBitLenOfShift32Bits;
3712 BitShiftOperand &bitShiftOpnd =
3713 CreateBitShiftOperand(BitShiftOperand::kLSL, static_cast<uint32>(tail0bitNum), bitLen);
3714 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopBsub, resOpnd, *opnd0Bak, regOpnd, bitShiftOpnd));
3715 return;
3716 }
3717
3718 SelectCopyImm(regOpnd, *immOpnd, primType);
3719 MOperator mOpCode = is64Bits ? MOP_xsubrrr : MOP_wsubrrr;
3720 Insn &newInsn = GetInsnBuilder()->BuildInsn(mOpCode, resOpnd, *opnd0Bak, regOpnd);
3721 GetCurBB()->AppendInsn(newInsn);
3722 }
3723
SelectSub(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)3724 Operand *AArch64CGFunc::SelectSub(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
3725 {
3726 PrimType dtype = node.GetPrimType();
3727 bool isSigned = IsSignedInteger(dtype);
3728 uint32 dsize = GetPrimTypeBitSize(dtype);
3729 bool is64Bits = (dsize == k64BitSize);
3730 bool isFloat = IsPrimitiveFloat(dtype);
3731 RegOperand *resOpnd = nullptr;
3732 if (!IsPrimitiveVector(dtype)) {
3733 /* promoted type */
3734 PrimType primType =
3735 isFloat ? dtype : ((is64Bits ? (isSigned ? PTY_i64 : PTY_u64) : (isSigned ? PTY_i32 : PTY_u32)));
3736 resOpnd = &GetOrCreateResOperand(parent, primType);
3737 SelectSub(*resOpnd, opnd0, opnd1, primType);
3738 } else {
3739 /* vector operands */
3740 resOpnd =
3741 SelectVectorBinOp(dtype, &opnd0, node.Opnd(0)->GetPrimType(), &opnd1, node.Opnd(1)->GetPrimType(), OP_sub);
3742 }
3743 return resOpnd;
3744 }
3745
SelectMpy(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)3746 Operand *AArch64CGFunc::SelectMpy(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
3747 {
3748 PrimType dtype = node.GetPrimType();
3749 bool isSigned = IsSignedInteger(dtype);
3750 uint32 dsize = GetPrimTypeBitSize(dtype);
3751 bool is64Bits = (dsize == k64BitSize);
3752 bool isFloat = IsPrimitiveFloat(dtype);
3753 RegOperand *resOpnd = nullptr;
3754 if (!IsPrimitiveVector(dtype)) {
3755 /* promoted type */
3756 PrimType primType =
3757 isFloat ? dtype : ((is64Bits ? (isSigned ? PTY_i64 : PTY_u64) : (isSigned ? PTY_i32 : PTY_u32)));
3758 resOpnd = &GetOrCreateResOperand(parent, primType);
3759 SelectMpy(*resOpnd, opnd0, opnd1, primType);
3760 } else {
3761 resOpnd =
3762 SelectVectorBinOp(dtype, &opnd0, node.Opnd(0)->GetPrimType(), &opnd1, node.Opnd(1)->GetPrimType(), OP_mul);
3763 }
3764 return resOpnd;
3765 }
3766
SelectMpy(Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType)3767 void AArch64CGFunc::SelectMpy(Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType)
3768 {
3769 Operand::OperandType opnd0Type = opnd0.GetKind();
3770 Operand::OperandType opnd1Type = opnd1.GetKind();
3771 uint32 dsize = GetPrimTypeBitSize(primType);
3772 bool is64Bits = (dsize == k64BitSize);
3773
3774 if (((opnd0Type == Operand::kOpdImmediate) || (opnd0Type == Operand::kOpdOffset) ||
3775 (opnd1Type == Operand::kOpdImmediate) || (opnd1Type == Operand::kOpdOffset)) &&
3776 IsPrimitiveInteger(primType)) {
3777 ImmOperand *imm = ((opnd0Type == Operand::kOpdImmediate) || (opnd0Type == Operand::kOpdOffset))
3778 ? static_cast<ImmOperand *>(&opnd0)
3779 : static_cast<ImmOperand *>(&opnd1);
3780 Operand *otherOp =
3781 ((opnd0Type == Operand::kOpdImmediate) || (opnd0Type == Operand::kOpdOffset)) ? &opnd1 : &opnd0;
3782 int64 immValue = llabs(imm->GetValue());
3783 if (immValue != 0 && (static_cast<uint64>(immValue) & (static_cast<uint64>(immValue) - 1)) == 0) {
3784 /* immValue is 1 << n */
3785 if (otherOp->GetKind() != Operand::kOpdRegister) {
3786 otherOp = &SelectCopy(*otherOp, primType, primType);
3787 }
3788 int64 shiftVal = __builtin_ffsll(immValue);
3789 ImmOperand &shiftNum = CreateImmOperand(shiftVal - 1, dsize, false);
3790 SelectShift(resOpnd, *otherOp, shiftNum, kShiftLeft, primType);
3791 bool reachSignBit = (is64Bits && (shiftVal == k64BitSize)) || (!is64Bits && (shiftVal == k32BitSize));
3792 if (imm->GetValue() < 0 && !reachSignBit) {
3793 SelectNeg(resOpnd, resOpnd, primType);
3794 }
3795
3796 return;
3797 } else if (immValue > 2) { // immValue should larger than 2
3798 uint32 zeroNum = static_cast<uint32>(__builtin_ffsll(immValue) - 1);
3799 int64 headVal = static_cast<uint64>(immValue) >> zeroNum;
3800 /*
3801 * if (headVal - 1) & (headVal - 2) == 0, that is (immVal >> zeroNum) - 1 == 1 << n
3802 * otherOp * immVal = (otherOp * (immVal >> zeroNum) * (1 << zeroNum)
3803 * = (otherOp * ((immVal >> zeroNum) - 1) + otherOp) * (1 << zeroNum)
3804 */
3805 CHECK_FATAL(static_cast<uint64>(headVal) >= 2, "value overflow");
3806 // 2 see comment above
3807 if (((static_cast<uint64>(headVal) - 1) & (static_cast<uint64>(headVal) - 2)) == 0) {
3808 if (otherOp->GetKind() != Operand::kOpdRegister) {
3809 otherOp = &SelectCopy(*otherOp, primType, primType);
3810 }
3811 ImmOperand &shiftNum1 = CreateImmOperand(__builtin_ffsll(headVal - 1) - 1, dsize, false);
3812 RegOperand &tmpOpnd = CreateRegisterOperandOfType(primType);
3813 SelectShift(tmpOpnd, *otherOp, shiftNum1, kShiftLeft, primType);
3814 SelectAdd(resOpnd, *otherOp, tmpOpnd, primType);
3815 ImmOperand &shiftNum2 = CreateImmOperand(zeroNum, dsize, false);
3816 SelectShift(resOpnd, resOpnd, shiftNum2, kShiftLeft, primType);
3817 if (imm->GetValue() < 0) {
3818 SelectNeg(resOpnd, resOpnd, primType);
3819 }
3820
3821 return;
3822 }
3823 }
3824 }
3825
3826 if ((opnd0Type != Operand::kOpdRegister) && (opnd1Type != Operand::kOpdRegister)) {
3827 SelectMpy(resOpnd, SelectCopy(opnd0, primType, primType), opnd1, primType);
3828 } else if ((opnd0Type == Operand::kOpdRegister) && (opnd1Type != Operand::kOpdRegister)) {
3829 SelectMpy(resOpnd, opnd0, SelectCopy(opnd1, primType, primType), primType);
3830 } else if ((opnd0Type != Operand::kOpdRegister) && (opnd1Type == Operand::kOpdRegister)) {
3831 SelectMpy(resOpnd, opnd1, opnd0, primType);
3832 } else {
3833 DEBUG_ASSERT(IsPrimitiveFloat(primType) || IsPrimitiveInteger(primType), "NYI Mpy");
3834 MOperator mOp =
3835 IsPrimitiveFloat(primType) ? (is64Bits ? MOP_xvmuld : MOP_xvmuls) : (is64Bits ? MOP_xmulrrr : MOP_wmulrrr);
3836 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opnd0, opnd1));
3837 }
3838 }
3839
SelectDiv(Operand & resOpnd,Operand & origOpnd0,Operand & opnd1,PrimType primType)3840 void AArch64CGFunc::SelectDiv(Operand &resOpnd, Operand &origOpnd0, Operand &opnd1, PrimType primType)
3841 {
3842 Operand &opnd0 = LoadIntoRegister(origOpnd0, primType);
3843 Operand::OperandType opnd0Type = opnd0.GetKind();
3844 Operand::OperandType opnd1Type = opnd1.GetKind();
3845 uint32 dsize = GetPrimTypeBitSize(primType);
3846 bool is64Bits = (dsize == k64BitSize);
3847
3848 if (Globals::GetInstance()->GetOptimLevel() > CGOptions::kLevel0) {
3849 if (((opnd1Type == Operand::kOpdImmediate) || (opnd1Type == Operand::kOpdOffset)) &&
3850 IsSignedInteger(primType)) {
3851 ImmOperand *imm = static_cast<ImmOperand *>(&opnd1);
3852 int64 immValue = llabs(imm->GetValue());
3853 if ((immValue != 0) && (static_cast<uint64>(immValue) & (static_cast<uint64>(immValue) - 1)) == 0) {
3854 if (immValue == 1) {
3855 if (imm->GetValue() > 0) {
3856 uint32 mOp = is64Bits ? MOP_xmovrr : MOP_wmovrr;
3857 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opnd0));
3858 } else {
3859 SelectNeg(resOpnd, opnd0, primType);
3860 }
3861
3862 return;
3863 }
3864 int32 shiftNumber = __builtin_ffsll(immValue) - 1;
3865 ImmOperand &shiftNum = CreateImmOperand(shiftNumber, dsize, false);
3866 Operand &tmpOpnd = CreateRegisterOperandOfType(primType);
3867 SelectShift(tmpOpnd, opnd0, CreateImmOperand(dsize - 1, dsize, false), kShiftAright, primType);
3868 uint32 mopBadd = is64Bits ? MOP_xaddrrrs : MOP_waddrrrs;
3869 int32 bitLen = is64Bits ? kBitLenOfShift64Bits : kBitLenOfShift32Bits;
3870 BitShiftOperand &shiftOpnd = CreateBitShiftOperand(BitShiftOperand::kLSR, dsize - shiftNumber, bitLen);
3871 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopBadd, tmpOpnd, opnd0, tmpOpnd, shiftOpnd));
3872 SelectShift(resOpnd, tmpOpnd, shiftNum, kShiftAright, primType);
3873 if (imm->GetValue() < 0) {
3874 SelectNeg(resOpnd, resOpnd, primType);
3875 }
3876
3877 return;
3878 }
3879 } else if (((opnd1Type == Operand::kOpdImmediate) || (opnd1Type == Operand::kOpdOffset)) &&
3880 IsUnsignedInteger(primType)) {
3881 ImmOperand *imm = static_cast<ImmOperand *>(&opnd1);
3882 if (imm->GetValue() != 0) {
3883 if ((imm->GetValue() > 0) &&
3884 ((static_cast<uint64>(imm->GetValue()) & (static_cast<uint64>(imm->GetValue()) - 1)) == 0)) {
3885 ImmOperand &shiftNum = CreateImmOperand(__builtin_ffsll(imm->GetValue()) - 1, dsize, false);
3886 SelectShift(resOpnd, opnd0, shiftNum, kShiftLright, primType);
3887
3888 return;
3889 } else if (imm->GetValue() < 0) {
3890 SelectAArch64Cmp(opnd0, *imm, true, dsize);
3891 SelectAArch64CSet(resOpnd, GetCondOperand(CC_CS), is64Bits);
3892
3893 return;
3894 }
3895 }
3896 }
3897 }
3898
3899 if (opnd0Type != Operand::kOpdRegister) {
3900 SelectDiv(resOpnd, SelectCopy(opnd0, primType, primType), opnd1, primType);
3901 } else if (opnd1Type != Operand::kOpdRegister) {
3902 SelectDiv(resOpnd, opnd0, SelectCopy(opnd1, primType, primType), primType);
3903 } else {
3904 DEBUG_ASSERT(IsPrimitiveFloat(primType) || IsPrimitiveInteger(primType), "NYI Div");
3905 MOperator mOp = IsPrimitiveFloat(primType)
3906 ? (is64Bits ? MOP_ddivrrr : MOP_sdivrrr)
3907 : (IsSignedInteger(primType) ? (is64Bits ? MOP_xsdivrrr : MOP_wsdivrrr)
3908 : (is64Bits ? MOP_xudivrrr : MOP_wudivrrr));
3909 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opnd0, opnd1));
3910 }
3911 }
3912
SelectDiv(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)3913 Operand *AArch64CGFunc::SelectDiv(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
3914 {
3915 PrimType dtype = node.GetPrimType();
3916 bool isSigned = IsSignedInteger(dtype);
3917 uint32 dsize = GetPrimTypeBitSize(dtype);
3918 bool is64Bits = (dsize == k64BitSize);
3919 bool isFloat = IsPrimitiveFloat(dtype);
3920 CHECK_FATAL(!IsPrimitiveVector(dtype), "NYI DIV vector operands");
3921 /* promoted type */
3922 PrimType primType =
3923 isFloat ? dtype : ((is64Bits ? (isSigned ? PTY_i64 : PTY_u64) : (isSigned ? PTY_i32 : PTY_u32)));
3924 RegOperand &resOpnd = GetOrCreateResOperand(parent, primType);
3925 SelectDiv(resOpnd, opnd0, opnd1, primType);
3926 return &resOpnd;
3927 }
3928
SelectRem(Operand & resOpnd,Operand & lhsOpnd,Operand & rhsOpnd,PrimType primType,bool isSigned,bool is64Bits)3929 void AArch64CGFunc::SelectRem(Operand &resOpnd, Operand &lhsOpnd, Operand &rhsOpnd, PrimType primType, bool isSigned,
3930 bool is64Bits)
3931 {
3932 Operand &opnd0 = LoadIntoRegister(lhsOpnd, primType);
3933 Operand &opnd1 = LoadIntoRegister(rhsOpnd, primType);
3934
3935 DEBUG_ASSERT(IsPrimitiveInteger(primType), "Wrong type for REM");
3936 /*
3937 * printf("%d \n", 29 % 7 );
3938 * -> 1
3939 * printf("%u %d \n", (unsigned)-7, (unsigned)(-7) % 7 );
3940 * -> 4294967289 4
3941 * printf("%d \n", (-7) % 7 );
3942 * -> 0
3943 * printf("%d \n", 237 % -7 );
3944 * 6->
3945 * printf("implicit i->u conversion %d \n", ((unsigned)237) % -7 );
3946 * implicit conversion 237
3947
3948 * http://stackoverflow.com/questions/35351470/obtaining-remainder-using-single-aarch64-instruction
3949 * input: x0=dividend, x1=divisor
3950 * udiv|sdiv x2, x0, x1
3951 * msub x3, x2, x1, x0 -- multply-sub : x3 <- x0 - x2*x1
3952 * result: x2=quotient, x3=remainder
3953 *
3954 * allocate temporary register
3955 */
3956 RegOperand &temp = CreateRegisterOperandOfType(primType);
3957 /*
3958 * mov w1, #2
3959 * sdiv wTemp, w0, w1
3960 * msub wRespond, wTemp, w1, w0
3961 * ========>
3962 * asr wTemp, w0, #31
3963 * lsr wTemp, wTemp, #31 (#30 for 4, #29 for 8, ...)
3964 * add wRespond, w0, wTemp
3965 * and wRespond, wRespond, #1 (#3 for 4, #7 for 8, ...)
3966 * sub wRespond, wRespond, w2
3967 *
3968 * if divde by 2
3969 * ========>
3970 * lsr wTemp, w0, #31
3971 * add wRespond, w0, wTemp
3972 * and wRespond, wRespond, #1
3973 * sub wRespond, wRespond, w2
3974 *
3975 * for unsigned rem op, just use and
3976 */
3977 if ((Globals::GetInstance()->GetOptimLevel() >= CGOptions::kLevel2)) {
3978 ImmOperand *imm = nullptr;
3979 Insn *movImmInsn = GetCurBB()->GetLastMachineInsn();
3980 if (movImmInsn &&
3981 ((movImmInsn->GetMachineOpcode() == MOP_wmovri32) || (movImmInsn->GetMachineOpcode() == MOP_xmovri64)) &&
3982 movImmInsn->GetOperand(0).Equals(opnd1)) {
3983 /*
3984 * mov w1, #2
3985 * rem res, w0, w1
3986 */
3987 imm = static_cast<ImmOperand *>(&movImmInsn->GetOperand(kInsnSecondOpnd));
3988 } else if (opnd1.IsImmediate()) {
3989 /*
3990 * rem res, w0, #2
3991 */
3992 imm = static_cast<ImmOperand *>(&opnd1);
3993 }
3994 /* positive or negative do not have effect on the result */
3995 int64 dividor = 0;
3996 if (imm && (imm->GetValue() != LONG_MIN)) {
3997 dividor = abs(imm->GetValue());
3998 }
3999 const int64 Log2OfDividor = GetLog2(static_cast<uint64>(dividor));
4000 if ((dividor != 0) && (Log2OfDividor > 0)) {
4001 if (is64Bits) {
4002 CHECK_FATAL(Log2OfDividor < k64BitSize, "imm out of bound");
4003 if (isSigned) {
4004 ImmOperand &rightShiftValue = CreateImmOperand(k64BitSize - Log2OfDividor, k64BitSize, isSigned);
4005 if (Log2OfDividor != 1) {
4006 /* 63->shift ALL , 32 ->32bit register */
4007 ImmOperand &rightShiftAll = CreateImmOperand(63, k64BitSize, isSigned);
4008 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xasrrri6, temp, opnd0, rightShiftAll));
4009
4010 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xlsrrri6, temp, temp, rightShiftValue));
4011 } else {
4012 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xlsrrri6, temp, opnd0, rightShiftValue));
4013 }
4014 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xaddrrr, resOpnd, opnd0, temp));
4015 ImmOperand &remBits = CreateImmOperand(dividor - 1, k64BitSize, isSigned);
4016 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xandrri13, resOpnd, resOpnd, remBits));
4017 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xsubrrr, resOpnd, resOpnd, temp));
4018 return;
4019 } else if (imm && imm->GetValue() > 0) {
4020 ImmOperand &remBits = CreateImmOperand(dividor - 1, k64BitSize, isSigned);
4021 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xandrri13, resOpnd, opnd0, remBits));
4022 return;
4023 }
4024 } else {
4025 CHECK_FATAL(Log2OfDividor < k32BitSize, "imm out of bound");
4026 if (isSigned) {
4027 ImmOperand &rightShiftValue = CreateImmOperand(k32BitSize - Log2OfDividor, k32BitSize, isSigned);
4028 if (Log2OfDividor != 1) {
4029 /* 31->shift ALL , 32 ->32bit register */
4030 ImmOperand &rightShiftAll = CreateImmOperand(31, k32BitSize, isSigned);
4031 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wasrrri5, temp, opnd0, rightShiftAll));
4032
4033 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wlsrrri5, temp, temp, rightShiftValue));
4034 } else {
4035 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wlsrrri5, temp, opnd0, rightShiftValue));
4036 }
4037
4038 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_waddrrr, resOpnd, opnd0, temp));
4039 ImmOperand &remBits = CreateImmOperand(dividor - 1, k32BitSize, isSigned);
4040 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wandrri12, resOpnd, resOpnd, remBits));
4041
4042 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wsubrrr, resOpnd, resOpnd, temp));
4043 return;
4044 } else if (imm && imm->GetValue() > 0) {
4045 ImmOperand &remBits = CreateImmOperand(dividor - 1, k32BitSize, isSigned);
4046 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wandrri12, resOpnd, opnd0, remBits));
4047 return;
4048 }
4049 }
4050 }
4051 }
4052
4053 uint32 mopDiv = is64Bits ? (isSigned ? MOP_xsdivrrr : MOP_xudivrrr) : (isSigned ? MOP_wsdivrrr : MOP_wudivrrr);
4054 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopDiv, temp, opnd0, opnd1));
4055
4056 uint32 mopSub = is64Bits ? MOP_xmsubrrrr : MOP_wmsubrrrr;
4057 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopSub, resOpnd, temp, opnd1, opnd0));
4058 }
4059
SelectRem(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)4060 Operand *AArch64CGFunc::SelectRem(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
4061 {
4062 PrimType dtype = node.GetPrimType();
4063 DEBUG_ASSERT(IsPrimitiveInteger(dtype), "wrong type for rem");
4064 bool isSigned = IsSignedInteger(dtype);
4065 uint32 dsize = GetPrimTypeBitSize(dtype);
4066 bool is64Bits = (dsize == k64BitSize);
4067 CHECK_FATAL(!IsPrimitiveVector(dtype), "NYI DIV vector operands");
4068
4069 /* promoted type */
4070 PrimType primType = ((is64Bits ? (isSigned ? PTY_i64 : PTY_u64) : (isSigned ? PTY_i32 : PTY_u32)));
4071 RegOperand &resOpnd = GetOrCreateResOperand(parent, primType);
4072 SelectRem(resOpnd, opnd0, opnd1, primType, isSigned, is64Bits);
4073 return &resOpnd;
4074 }
4075
SelectLand(BinaryNode & node,Operand & lhsOpnd,Operand & rhsOpnd,const BaseNode & parent)4076 Operand *AArch64CGFunc::SelectLand(BinaryNode &node, Operand &lhsOpnd, Operand &rhsOpnd, const BaseNode &parent)
4077 {
4078 PrimType primType = node.GetPrimType();
4079 DEBUG_ASSERT(IsPrimitiveInteger(primType), "Land should be integer type");
4080 bool is64Bits = (GetPrimTypeBitSize(primType) == k64BitSize);
4081 RegOperand &resOpnd = GetOrCreateResOperand(parent, is64Bits ? PTY_u64 : PTY_u32);
4082 /*
4083 * OP0 band Op1
4084 * cmp OP0, 0 # compare X0 with 0, sets Z bit
4085 * ccmp OP1, 0, 4 //==0100b, ne # if(OP0!=0) cmp Op1 and 0, else NZCV <- 0100 makes OP0==0
4086 * cset RES, ne # if Z==1(i.e., OP0==0||OP1==0) RES<-0, RES<-1
4087 */
4088 Operand &opnd0 = LoadIntoRegister(lhsOpnd, primType);
4089 SelectAArch64Cmp(opnd0, CreateImmOperand(0, primType, false), true, GetPrimTypeBitSize(primType));
4090 Operand &opnd1 = LoadIntoRegister(rhsOpnd, primType);
4091 constexpr int64 immValue4 = 4; // integer as comment above
4092 SelectAArch64CCmp(opnd1, CreateImmOperand(0, primType, false), CreateImmOperand(immValue4, PTY_u8, false),
4093 GetCondOperand(CC_NE), is64Bits);
4094 SelectAArch64CSet(resOpnd, GetCondOperand(CC_NE), is64Bits);
4095 return &resOpnd;
4096 }
4097
SelectLor(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent,bool parentIsBr)4098 Operand *AArch64CGFunc::SelectLor(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent,
4099 bool parentIsBr)
4100 {
4101 PrimType primType = node.GetPrimType();
4102 DEBUG_ASSERT(IsPrimitiveInteger(primType), "Lior should be integer type");
4103 bool is64Bits = (GetPrimTypeBitSize(primType) == k64BitSize);
4104 RegOperand &resOpnd = GetOrCreateResOperand(parent, is64Bits ? PTY_u64 : PTY_u32);
4105 /*
4106 * OP0 band Op1
4107 * cmp OP0, 0 # compare X0 with 0, sets Z bit
4108 * ccmp OP1, 0, 0 //==0100b, eq # if(OP0==0,eq) cmp Op1 and 0, else NZCV <- 0000 makes OP0!=0
4109 * cset RES, ne # if Z==1(i.e., OP0==0&&OP1==0) RES<-0, RES<-1
4110 */
4111 if (parentIsBr && !is64Bits && opnd0.IsRegister() && (static_cast<RegOperand *>(&opnd0)->GetValidBitsNum() == 1) &&
4112 opnd1.IsRegister() && (static_cast<RegOperand *>(&opnd1)->GetValidBitsNum() == 1)) {
4113 uint32 mOp = MOP_wiorrrr;
4114 static_cast<RegOperand &>(resOpnd).SetValidBitsNum(1);
4115 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opnd0, opnd1));
4116 } else {
4117 SelectBior(resOpnd, opnd0, opnd1, primType);
4118 SelectAArch64Cmp(resOpnd, CreateImmOperand(0, primType, false), true, GetPrimTypeBitSize(primType));
4119 SelectAArch64CSet(resOpnd, GetCondOperand(CC_NE), is64Bits);
4120 }
4121 return &resOpnd;
4122 }
4123
SelectCmpOp(Operand & resOpnd,Operand & lhsOpnd,Operand & rhsOpnd,Opcode opcode,PrimType primType,const BaseNode & parent)4124 void AArch64CGFunc::SelectCmpOp(Operand &resOpnd, Operand &lhsOpnd, Operand &rhsOpnd, Opcode opcode, PrimType primType,
4125 const BaseNode &parent)
4126 {
4127 uint32 dsize = resOpnd.GetSize();
4128 bool isFloat = IsPrimitiveFloat(primType);
4129 Operand &opnd0 = LoadIntoRegister(lhsOpnd, primType);
4130
4131 /*
4132 * most of FP constants are passed as MemOperand
4133 * except 0.0 which is passed as kOpdFPImmediate
4134 */
4135 Operand::OperandType opnd1Type = rhsOpnd.GetKind();
4136 Operand *opnd1 = &rhsOpnd;
4137 if ((opnd1Type != Operand::kOpdImmediate) && (opnd1Type != Operand::kOpdFPImmediate) &&
4138 (opnd1Type != Operand::kOpdOffset)) {
4139 opnd1 = &LoadIntoRegister(rhsOpnd, primType);
4140 }
4141
4142 bool unsignedIntegerComparison = !isFloat && !IsSignedInteger(primType);
4143 /*
4144 * OP_cmp, OP_cmpl, OP_cmpg
4145 * <cmp> OP0, OP1 ; fcmp for OP_cmpl/OP_cmpg, cmp/fcmpe for OP_cmp
4146 * CSINV RES, WZR, WZR, GE
4147 * CSINC RES, RES, WZR, LE
4148 * if OP_cmpl, CSINV RES, RES, WZR, VC (no overflow)
4149 * if OP_cmpg, CSINC RES, RES, WZR, VC (no overflow)
4150 */
4151 RegOperand &xzr = GetZeroOpnd(dsize);
4152 if ((opcode == OP_cmpl) || (opcode == OP_cmpg)) {
4153 DEBUG_ASSERT(isFloat, "incorrect operand types");
4154 SelectTargetFPCmpQuiet(opnd0, *opnd1, GetPrimTypeBitSize(primType));
4155 SelectAArch64CSINV(resOpnd, xzr, xzr, GetCondOperand(CC_GE), (dsize == k64BitSize));
4156 SelectAArch64CSINC(resOpnd, resOpnd, xzr, GetCondOperand(CC_LE), (dsize == k64BitSize));
4157 if (opcode == OP_cmpl) {
4158 SelectAArch64CSINV(resOpnd, resOpnd, xzr, GetCondOperand(CC_VC), (dsize == k64BitSize));
4159 } else {
4160 SelectAArch64CSINC(resOpnd, resOpnd, xzr, GetCondOperand(CC_VC), (dsize == k64BitSize));
4161 }
4162 return;
4163 }
4164
4165 if (opcode == OP_cmp) {
4166 SelectAArch64Cmp(opnd0, *opnd1, !isFloat, GetPrimTypeBitSize(primType));
4167 if (unsignedIntegerComparison) {
4168 SelectAArch64CSINV(resOpnd, xzr, xzr, GetCondOperand(CC_HS), (dsize == k64BitSize));
4169 SelectAArch64CSINC(resOpnd, resOpnd, xzr, GetCondOperand(CC_LS), (dsize == k64BitSize));
4170 } else {
4171 SelectAArch64CSINV(resOpnd, xzr, xzr, GetCondOperand(CC_GE), (dsize == k64BitSize));
4172 SelectAArch64CSINC(resOpnd, resOpnd, xzr, GetCondOperand(CC_LE), (dsize == k64BitSize));
4173 }
4174 return;
4175 }
4176
4177 // lt u8 i32 ( xxx, 0 ) => get sign bit
4178 if ((opcode == OP_lt) && opnd0.IsRegister() && opnd1->IsImmediate() &&
4179 (static_cast<ImmOperand *>(opnd1)->GetValue() == 0) && parent.GetOpCode() != OP_select && !isFloat) {
4180 bool is64Bits = (opnd0.GetSize() == k64BitSize);
4181 if (!unsignedIntegerComparison) {
4182 int32 bitLen = is64Bits ? kBitLenOfShift64Bits : kBitLenOfShift32Bits;
4183 ImmOperand &shiftNum = CreateImmOperand(is64Bits ? kHighestBitOf64Bits : kHighestBitOf32Bits,
4184 static_cast<uint32>(bitLen), false);
4185 MOperator mOpCode = is64Bits ? MOP_xlsrrri6 : MOP_wlsrrri5;
4186 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, resOpnd, opnd0, shiftNum));
4187 return;
4188 }
4189 ImmOperand &constNum = CreateImmOperand(0, is64Bits ? k64BitSize : k32BitSize, false);
4190 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(is64Bits ? MOP_xmovri64 : MOP_wmovri32, resOpnd, constNum));
4191 return;
4192 }
4193 SelectAArch64Cmp(opnd0, *opnd1, !isFloat, GetPrimTypeBitSize(primType));
4194
4195 ConditionCode cc = CC_EQ;
4196 // need to handle unordered situation here.
4197 switch (opcode) {
4198 case OP_eq:
4199 cc = CC_EQ;
4200 break;
4201 case OP_ne:
4202 cc = isFloat ? CC_MI : CC_NE;
4203 break;
4204 case OP_le:
4205 cc = isFloat ? CC_LS : unsignedIntegerComparison ? CC_LS : CC_LE;
4206 break;
4207 case OP_ge:
4208 cc = unsignedIntegerComparison ? CC_HS : CC_GE;
4209 break;
4210 case OP_gt:
4211 cc = unsignedIntegerComparison ? CC_HI : CC_GT;
4212 break;
4213 case OP_lt:
4214 cc = isFloat ? CC_MI : unsignedIntegerComparison ? CC_LO : CC_LT;
4215 break;
4216 default:
4217 CHECK_FATAL(false, "illegal logical operator");
4218 }
4219 SelectAArch64CSet(resOpnd, GetCondOperand(cc), (dsize == k64BitSize));
4220 if (isFloat && opcode == OP_ne) {
4221 SelectAArch64CSINC(resOpnd, resOpnd, xzr, GetCondOperand(CC_LE), (dsize == k64BitSize));
4222 }
4223 }
4224
SelectCmpOp(CompareNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)4225 Operand *AArch64CGFunc::SelectCmpOp(CompareNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
4226 {
4227 RegOperand *resOpnd = nullptr;
4228 if (!IsPrimitiveVector(node.GetPrimType())) {
4229 resOpnd = &GetOrCreateResOperand(parent, node.GetPrimType());
4230 SelectCmpOp(*resOpnd, opnd0, opnd1, node.GetOpCode(), node.GetOpndType(), parent);
4231 } else {
4232 resOpnd = SelectVectorCompare(&opnd0, node.Opnd(0)->GetPrimType(), &opnd1, node.Opnd(1)->GetPrimType(),
4233 node.GetOpCode());
4234 }
4235 return resOpnd;
4236 }
4237
SelectTargetFPCmpQuiet(Operand & o0,Operand & o1,uint32 dsize)4238 void AArch64CGFunc::SelectTargetFPCmpQuiet(Operand &o0, Operand &o1, uint32 dsize)
4239 {
4240 MOperator mOpCode = 0;
4241 if (o1.GetKind() == Operand::kOpdFPImmediate) {
4242 CHECK_FATAL(static_cast<ImmOperand &>(o0).GetValue() == 0, "NIY");
4243 mOpCode = (dsize == k64BitSize) ? MOP_dcmpqri : (dsize == k32BitSize) ? MOP_scmpqri : MOP_hcmpqri;
4244 } else if (o1.GetKind() == Operand::kOpdRegister) {
4245 mOpCode = (dsize == k64BitSize) ? MOP_dcmpqrr : (dsize == k32BitSize) ? MOP_scmpqrr : MOP_hcmpqrr;
4246 } else {
4247 CHECK_FATAL(false, "unsupported operand type");
4248 }
4249 Operand &rflag = GetOrCreateRflag();
4250 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, rflag, o0, o1));
4251 }
4252
SelectAArch64Cmp(Operand & o0,Operand & o1,bool isIntType,uint32 dsize)4253 void AArch64CGFunc::SelectAArch64Cmp(Operand &o0, Operand &o1, bool isIntType, uint32 dsize)
4254 {
4255 MOperator mOpCode = 0;
4256 Operand *newO1 = &o1;
4257 if (isIntType) {
4258 if ((o1.GetKind() == Operand::kOpdImmediate) || (o1.GetKind() == Operand::kOpdOffset)) {
4259 ImmOperand *immOpnd = static_cast<ImmOperand *>(&o1);
4260 /*
4261 * imm : 0 ~ 4095, shift: none, LSL #0, or LSL #12
4262 * aarch64 assembly takes up to 24-bits, if the lower 12 bits is all 0
4263 */
4264 if (immOpnd->IsInBitSize(kMaxImmVal12Bits, 0) || immOpnd->IsInBitSize(kMaxImmVal12Bits, kMaxImmVal12Bits)) {
4265 mOpCode = (dsize == k64BitSize) ? MOP_xcmpri : MOP_wcmpri;
4266 } else {
4267 /* load into register */
4268 PrimType ptype = (dsize == k64BitSize) ? PTY_i64 : PTY_i32;
4269 newO1 = &SelectCopy(o1, ptype, ptype);
4270 mOpCode = (dsize == k64BitSize) ? MOP_xcmprr : MOP_wcmprr;
4271 }
4272 } else if (o1.GetKind() == Operand::kOpdRegister) {
4273 mOpCode = (dsize == k64BitSize) ? MOP_xcmprr : MOP_wcmprr;
4274 } else {
4275 CHECK_FATAL(false, "unsupported operand type");
4276 }
4277 } else { /* float */
4278 if (o1.GetKind() == Operand::kOpdFPImmediate) {
4279 CHECK_FATAL(static_cast<ImmOperand &>(o1).GetValue() == 0, "NIY");
4280 mOpCode = (dsize == k64BitSize) ? MOP_dcmperi : ((dsize == k32BitSize) ? MOP_scmperi : MOP_hcmperi);
4281 } else if (o1.GetKind() == Operand::kOpdRegister) {
4282 mOpCode = (dsize == k64BitSize) ? MOP_dcmperr : ((dsize == k32BitSize) ? MOP_scmperr : MOP_hcmperr);
4283 } else {
4284 CHECK_FATAL(false, "unsupported operand type");
4285 }
4286 }
4287 DEBUG_ASSERT(mOpCode != 0, "mOpCode undefined");
4288 Operand &rflag = GetOrCreateRflag();
4289 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, rflag, o0, *newO1));
4290 }
4291
SelectAArch64CCmp(Operand & o,Operand & i,Operand & nzcv,CondOperand & cond,bool is64Bits)4292 void AArch64CGFunc::SelectAArch64CCmp(Operand &o, Operand &i, Operand &nzcv, CondOperand &cond, bool is64Bits)
4293 {
4294 uint32 mOpCode = is64Bits ? MOP_xccmpriic : MOP_wccmpriic;
4295 Operand &rflag = GetOrCreateRflag();
4296 std::vector<Operand *> opndVec;
4297 opndVec.push_back(&rflag);
4298 opndVec.push_back(&o);
4299 opndVec.push_back(&i);
4300 opndVec.push_back(&nzcv);
4301 opndVec.push_back(&cond);
4302 opndVec.push_back(&rflag);
4303 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, opndVec));
4304 }
4305
SelectAArch64CSet(Operand & r,CondOperand & cond,bool is64Bits)4306 void AArch64CGFunc::SelectAArch64CSet(Operand &r, CondOperand &cond, bool is64Bits)
4307 {
4308 MOperator mOpCode = is64Bits ? MOP_xcsetrc : MOP_wcsetrc;
4309 Operand &rflag = GetOrCreateRflag();
4310 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, r, cond, rflag));
4311 }
4312
SelectAArch64CSINV(Operand & res,Operand & o0,Operand & o1,CondOperand & cond,bool is64Bits)4313 void AArch64CGFunc::SelectAArch64CSINV(Operand &res, Operand &o0, Operand &o1, CondOperand &cond, bool is64Bits)
4314 {
4315 MOperator mOpCode = is64Bits ? MOP_xcsinvrrrc : MOP_wcsinvrrrc;
4316 Operand &rflag = GetOrCreateRflag();
4317 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, res, o0, o1, cond, rflag));
4318 }
4319
SelectAArch64CSINC(Operand & res,Operand & o0,Operand & o1,CondOperand & cond,bool is64Bits)4320 void AArch64CGFunc::SelectAArch64CSINC(Operand &res, Operand &o0, Operand &o1, CondOperand &cond, bool is64Bits)
4321 {
4322 MOperator mOpCode = is64Bits ? MOP_xcsincrrrc : MOP_wcsincrrrc;
4323 Operand &rflag = GetOrCreateRflag();
4324 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, res, o0, o1, cond, rflag));
4325 }
4326
SelectBand(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)4327 Operand *AArch64CGFunc::SelectBand(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
4328 {
4329 return SelectRelationOperator(kAND, node, opnd0, opnd1, parent);
4330 }
4331
SelectBand(Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType)4332 void AArch64CGFunc::SelectBand(Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType)
4333 {
4334 SelectRelationOperator(kAND, resOpnd, opnd0, opnd1, primType);
4335 }
4336
SelectRelationOperator(RelationOperator operatorCode,const BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)4337 Operand *AArch64CGFunc::SelectRelationOperator(RelationOperator operatorCode, const BinaryNode &node, Operand &opnd0,
4338 Operand &opnd1, const BaseNode &parent)
4339 {
4340 PrimType dtype = node.GetPrimType();
4341 bool isSigned = IsSignedInteger(dtype);
4342 uint32 dsize = GetPrimTypeBitSize(dtype);
4343 bool is64Bits = (dsize == k64BitSize);
4344 RegOperand *resOpnd = nullptr;
4345 if (!IsPrimitiveVector(dtype)) {
4346 PrimType primType =
4347 is64Bits ? (isSigned ? PTY_i64 : PTY_u64) : (isSigned ? PTY_i32 : PTY_u32); /* promoted type */
4348 resOpnd = &GetOrCreateResOperand(parent, primType);
4349 SelectRelationOperator(operatorCode, *resOpnd, opnd0, opnd1, primType);
4350 } else {
4351 /* vector operations */
4352 resOpnd = SelectVectorBitwiseOp(dtype, &opnd0, node.Opnd(0)->GetPrimType(), &opnd1, node.Opnd(1)->GetPrimType(),
4353 (operatorCode == kAND) ? OP_band : (operatorCode == kIOR ? OP_bior : OP_bxor));
4354 }
4355 return resOpnd;
4356 }
4357
SelectRelationMop(RelationOperator operatorCode,RelationOperatorOpndPattern opndPattern,bool is64Bits,bool isBitmaskImmediate,bool isBitNumLessThan16) const4358 MOperator AArch64CGFunc::SelectRelationMop(RelationOperator operatorCode, RelationOperatorOpndPattern opndPattern,
4359 bool is64Bits, bool isBitmaskImmediate, bool isBitNumLessThan16) const
4360 {
4361 MOperator mOp = MOP_undef;
4362 if (opndPattern == kRegReg) {
4363 switch (operatorCode) {
4364 case kAND:
4365 mOp = is64Bits ? MOP_xandrrr : MOP_wandrrr;
4366 break;
4367 case kIOR:
4368 mOp = is64Bits ? MOP_xiorrrr : MOP_wiorrrr;
4369 break;
4370 case kEOR:
4371 mOp = is64Bits ? MOP_xeorrrr : MOP_weorrrr;
4372 break;
4373 default:
4374 break;
4375 }
4376 return mOp;
4377 }
4378 /* opndPattern == KRegImm */
4379 if (isBitmaskImmediate) {
4380 switch (operatorCode) {
4381 case kAND:
4382 mOp = is64Bits ? MOP_xandrri13 : MOP_wandrri12;
4383 break;
4384 case kIOR:
4385 mOp = is64Bits ? MOP_xiorrri13 : MOP_wiorrri12;
4386 break;
4387 case kEOR:
4388 mOp = is64Bits ? MOP_xeorrri13 : MOP_weorrri12;
4389 break;
4390 default:
4391 break;
4392 }
4393 return mOp;
4394 }
4395 /* normal imm value */
4396 if (isBitNumLessThan16) {
4397 switch (operatorCode) {
4398 case kAND:
4399 mOp = is64Bits ? MOP_xandrrrs : MOP_wandrrrs;
4400 break;
4401 case kIOR:
4402 mOp = is64Bits ? MOP_xiorrrrs : MOP_wiorrrrs;
4403 break;
4404 case kEOR:
4405 mOp = is64Bits ? MOP_xeorrrrs : MOP_weorrrrs;
4406 break;
4407 default:
4408 break;
4409 }
4410 return mOp;
4411 }
4412 return mOp;
4413 }
4414
SelectRelationOperator(RelationOperator operatorCode,Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType)4415 void AArch64CGFunc::SelectRelationOperator(RelationOperator operatorCode, Operand &resOpnd, Operand &opnd0,
4416 Operand &opnd1, PrimType primType)
4417 {
4418 Operand::OperandType opnd0Type = opnd0.GetKind();
4419 Operand::OperandType opnd1Type = opnd1.GetKind();
4420 uint32 dsize = GetPrimTypeBitSize(primType);
4421 bool is64Bits = (dsize == k64BitSize);
4422 /* op #imm. #imm */
4423 if ((opnd0Type != Operand::kOpdRegister) && (opnd1Type != Operand::kOpdRegister)) {
4424 SelectRelationOperator(operatorCode, resOpnd, SelectCopy(opnd0, primType, primType), opnd1, primType);
4425 return;
4426 }
4427 /* op #imm, reg -> op reg, #imm */
4428 if ((opnd0Type != Operand::kOpdRegister) && (opnd1Type == Operand::kOpdRegister)) {
4429 SelectRelationOperator(operatorCode, resOpnd, opnd1, opnd0, primType);
4430 return;
4431 }
4432 /* op reg, reg */
4433 if ((opnd0Type == Operand::kOpdRegister) && (opnd1Type == Operand::kOpdRegister)) {
4434 DEBUG_ASSERT(IsPrimitiveInteger(primType), "NYI band");
4435 MOperator mOp = SelectRelationMop(operatorCode, kRegReg, is64Bits, false, false);
4436 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opnd0, opnd1));
4437 return;
4438 }
4439 /* op reg, #imm */
4440 if ((opnd0Type == Operand::kOpdRegister) && (opnd1Type != Operand::kOpdRegister)) {
4441 if (!((opnd1Type == Operand::kOpdImmediate) || (opnd1Type == Operand::kOpdOffset))) {
4442 SelectRelationOperator(operatorCode, resOpnd, opnd0, SelectCopy(opnd1, primType, primType), primType);
4443 return;
4444 }
4445
4446 ImmOperand *immOpnd = static_cast<ImmOperand *>(&opnd1);
4447 if (immOpnd->IsZero()) {
4448 if (operatorCode == kAND) {
4449 uint32 mopMv = is64Bits ? MOP_xmovrr : MOP_wmovrr;
4450 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopMv, resOpnd, GetZeroOpnd(dsize)));
4451 } else if ((operatorCode == kIOR) || (operatorCode == kEOR)) {
4452 SelectCopy(resOpnd, primType, opnd0, primType);
4453 }
4454 } else if ((immOpnd->IsAllOnes()) || (!is64Bits && immOpnd->IsAllOnes32bit())) {
4455 if (operatorCode == kAND) {
4456 SelectCopy(resOpnd, primType, opnd0, primType);
4457 } else if (operatorCode == kIOR) {
4458 uint32 mopMovn = is64Bits ? MOP_xmovnri16 : MOP_wmovnri16;
4459 ImmOperand &src16 = CreateImmOperand(0, k16BitSize, false);
4460 BitShiftOperand *lslOpnd = GetLogicalShiftLeftOperand(0, is64Bits);
4461 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopMovn, resOpnd, src16, *lslOpnd));
4462 } else if (operatorCode == kEOR) {
4463 SelectMvn(resOpnd, opnd0, primType);
4464 }
4465 } else if (immOpnd->IsBitmaskImmediate()) {
4466 MOperator mOp = SelectRelationMop(operatorCode, kRegImm, is64Bits, true, false);
4467 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opnd0, opnd1));
4468 } else {
4469 int64 immVal = immOpnd->GetValue();
4470 int32 tail0BitNum = GetTail0BitNum(immVal);
4471 int32 head0BitNum = GetHead0BitNum(immVal);
4472 const int32 bitNum = (k64BitSizeInt - head0BitNum) - tail0BitNum;
4473 RegOperand ®Opnd = CreateRegisterOperandOfType(primType);
4474
4475 if (bitNum <= k16ValidBit) {
4476 int64 newImm = (static_cast<uint64>(immVal) >> static_cast<uint32>(tail0BitNum)) & 0xFFFF;
4477 ImmOperand &immOpnd1 = CreateImmOperand(newImm, k32BitSize, false);
4478 SelectCopyImm(regOpnd, immOpnd1, primType);
4479 MOperator mOp = SelectRelationMop(operatorCode, kRegImm, is64Bits, false, true);
4480 int32 bitLen = is64Bits ? kBitLenOfShift64Bits : kBitLenOfShift32Bits;
4481 BitShiftOperand &shiftOpnd =
4482 CreateBitShiftOperand(BitShiftOperand::kLSL, static_cast<uint32>(tail0BitNum), bitLen);
4483 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opnd0, regOpnd, shiftOpnd));
4484 } else {
4485 SelectCopyImm(regOpnd, *immOpnd, primType);
4486 MOperator mOp = SelectRelationMop(operatorCode, kRegReg, is64Bits, false, false);
4487 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opnd0, regOpnd));
4488 }
4489 }
4490 }
4491 }
4492
SelectBior(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)4493 Operand *AArch64CGFunc::SelectBior(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
4494 {
4495 return SelectRelationOperator(kIOR, node, opnd0, opnd1, parent);
4496 }
4497
SelectBior(Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType)4498 void AArch64CGFunc::SelectBior(Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType)
4499 {
4500 SelectRelationOperator(kIOR, resOpnd, opnd0, opnd1, primType);
4501 }
4502
SelectMinOrMax(bool isMin,const BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)4503 Operand *AArch64CGFunc::SelectMinOrMax(bool isMin, const BinaryNode &node, Operand &opnd0, Operand &opnd1,
4504 const BaseNode &parent)
4505 {
4506 PrimType dtype = node.GetPrimType();
4507 bool isSigned = IsSignedInteger(dtype);
4508 uint32 dsize = GetPrimTypeBitSize(dtype);
4509 bool is64Bits = (dsize == k64BitSize);
4510 bool isFloat = IsPrimitiveFloat(dtype);
4511 /* promoted type */
4512 PrimType primType = isFloat ? dtype : (is64Bits ? (isSigned ? PTY_i64 : PTY_u64) : (isSigned ? PTY_i32 : PTY_u32));
4513 RegOperand &resOpnd = GetOrCreateResOperand(parent, primType);
4514 SelectMinOrMax(isMin, resOpnd, opnd0, opnd1, primType);
4515 return &resOpnd;
4516 }
4517
SelectMinOrMax(bool isMin,Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType)4518 void AArch64CGFunc::SelectMinOrMax(bool isMin, Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType)
4519 {
4520 uint32 dsize = GetPrimTypeBitSize(primType);
4521 bool is64Bits = (dsize == k64BitSize);
4522 if (IsPrimitiveInteger(primType)) {
4523 RegOperand ®Opnd0 = LoadIntoRegister(opnd0, primType);
4524 Operand ®Opnd1 = LoadIntoRegister(opnd1, primType);
4525 SelectAArch64Cmp(regOpnd0, regOpnd1, true, dsize);
4526 Operand &newResOpnd = LoadIntoRegister(resOpnd, primType);
4527 if (isMin) {
4528 CondOperand &cc = IsSignedInteger(primType) ? GetCondOperand(CC_LT) : GetCondOperand(CC_LO);
4529 SelectAArch64Select(newResOpnd, regOpnd0, regOpnd1, cc, true, dsize);
4530 } else {
4531 CondOperand &cc = IsSignedInteger(primType) ? GetCondOperand(CC_GT) : GetCondOperand(CC_HI);
4532 SelectAArch64Select(newResOpnd, regOpnd0, regOpnd1, cc, true, dsize);
4533 }
4534 } else if (IsPrimitiveFloat(primType)) {
4535 RegOperand ®Opnd0 = LoadIntoRegister(opnd0, primType);
4536 RegOperand ®Opnd1 = LoadIntoRegister(opnd1, primType);
4537 SelectFMinFMax(resOpnd, regOpnd0, regOpnd1, is64Bits, isMin);
4538 } else {
4539 CHECK_FATAL(false, "NIY type max or min");
4540 }
4541 }
4542
SelectMin(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)4543 Operand *AArch64CGFunc::SelectMin(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
4544 {
4545 return SelectMinOrMax(true, node, opnd0, opnd1, parent);
4546 }
4547
SelectMin(Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType)4548 void AArch64CGFunc::SelectMin(Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType)
4549 {
4550 SelectMinOrMax(true, resOpnd, opnd0, opnd1, primType);
4551 }
4552
SelectMax(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)4553 Operand *AArch64CGFunc::SelectMax(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
4554 {
4555 return SelectMinOrMax(false, node, opnd0, opnd1, parent);
4556 }
4557
SelectMax(Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType)4558 void AArch64CGFunc::SelectMax(Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType)
4559 {
4560 SelectMinOrMax(false, resOpnd, opnd0, opnd1, primType);
4561 }
4562
SelectFMinFMax(Operand & resOpnd,Operand & opnd0,Operand & opnd1,bool is64Bits,bool isMin)4563 void AArch64CGFunc::SelectFMinFMax(Operand &resOpnd, Operand &opnd0, Operand &opnd1, bool is64Bits, bool isMin)
4564 {
4565 uint32 mOpCode = isMin ? (is64Bits ? MOP_xfminrrr : MOP_wfminrrr) : (is64Bits ? MOP_xfmaxrrr : MOP_wfmaxrrr);
4566 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, resOpnd, opnd0, opnd1));
4567 }
4568
SelectBxor(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)4569 Operand *AArch64CGFunc::SelectBxor(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
4570 {
4571 return SelectRelationOperator(kEOR, node, opnd0, opnd1, parent);
4572 }
4573
SelectBxor(Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType)4574 void AArch64CGFunc::SelectBxor(Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType)
4575 {
4576 SelectRelationOperator(kEOR, resOpnd, opnd0, opnd1, primType);
4577 }
4578
SelectShift(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)4579 Operand *AArch64CGFunc::SelectShift(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
4580 {
4581 PrimType dtype = node.GetPrimType();
4582 bool isSigned = IsSignedInteger(dtype);
4583 uint32 dsize = GetPrimTypeBitSize(dtype);
4584 bool is64Bits = (dsize == k64BitSize);
4585 bool isFloat = IsPrimitiveFloat(dtype);
4586 RegOperand *resOpnd = nullptr;
4587 Opcode opcode = node.GetOpCode();
4588
4589 bool isOneElemVector = false;
4590 BaseNode *expr = node.Opnd(0);
4591 if (expr->GetOpCode() == OP_dread) {
4592 MIRSymbol *symbol = GetFunction().GetLocalOrGlobalSymbol(static_cast<DreadNode *>(expr)->GetStIdx());
4593 isOneElemVector = symbol->GetAttr(ATTR_oneelem_simd);
4594 }
4595
4596 Operand *opd0 = &opnd0;
4597 PrimType otyp0 = expr->GetPrimType();
4598 if (IsPrimitiveVector(dtype) && opnd0.IsConstImmediate()) {
4599 opd0 = SelectVectorFromScalar(dtype, opd0, node.Opnd(0)->GetPrimType());
4600 otyp0 = dtype;
4601 }
4602
4603 if (IsPrimitiveVector(dtype) && opnd1.IsConstImmediate()) {
4604 int64 sConst = static_cast<ImmOperand &>(opnd1).GetValue();
4605 resOpnd = SelectVectorShiftImm(dtype, opd0, &opnd1, static_cast<int32>(sConst), opcode);
4606 } else if ((IsPrimitiveVector(dtype) || isOneElemVector) && !opnd1.IsConstImmediate()) {
4607 resOpnd = SelectVectorShift(dtype, opd0, otyp0, &opnd1, node.Opnd(1)->GetPrimType(), opcode);
4608 } else {
4609 PrimType primType =
4610 isFloat ? dtype : (is64Bits ? (isSigned ? PTY_i64 : PTY_u64) : (isSigned ? PTY_i32 : PTY_u32));
4611 resOpnd = &GetOrCreateResOperand(parent, primType);
4612 ShiftDirection direct = (opcode == OP_lshr) ? kShiftLright : ((opcode == OP_ashr) ? kShiftAright : kShiftLeft);
4613 SelectShift(*resOpnd, opnd0, opnd1, direct, primType);
4614 }
4615
4616 if (dtype == PTY_i16) {
4617 MOperator exOp = is64Bits ? MOP_xsxth64 : MOP_xsxth32;
4618 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(exOp, *resOpnd, *resOpnd));
4619 } else if (dtype == PTY_i8) {
4620 MOperator exOp = is64Bits ? MOP_xsxtb64 : MOP_xsxtb32;
4621 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(exOp, *resOpnd, *resOpnd));
4622 }
4623 return resOpnd;
4624 }
4625
SelectRor(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)4626 Operand *AArch64CGFunc::SelectRor(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
4627 {
4628 PrimType dtype = node.GetPrimType();
4629 uint32 dsize = GetPrimTypeBitSize(dtype);
4630 PrimType primType = (dsize == k64BitSize) ? PTY_u64 : PTY_u32;
4631 RegOperand *resOpnd = &GetOrCreateResOperand(parent, primType);
4632 Operand *firstOpnd = &LoadIntoRegister(opnd0, primType);
4633 MOperator mopRor = (dsize == k64BitSize) ? MOP_xrorrrr : MOP_wrorrrr;
4634 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopRor, *resOpnd, *firstOpnd, opnd1));
4635 return resOpnd;
4636 }
4637
SelectBxorShift(Operand & resOpnd,Operand * opnd0,Operand * opnd1,Operand & opnd2,PrimType primType)4638 void AArch64CGFunc::SelectBxorShift(Operand &resOpnd, Operand *opnd0, Operand *opnd1, Operand &opnd2, PrimType primType)
4639 {
4640 opnd0 = &LoadIntoRegister(*opnd0, primType);
4641 opnd1 = &LoadIntoRegister(*opnd1, primType);
4642 uint32 dsize = GetPrimTypeBitSize(primType);
4643 bool is64Bits = (dsize == k64BitSize);
4644 MOperator mopBxor = is64Bits ? MOP_xeorrrrs : MOP_weorrrrs;
4645 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopBxor, resOpnd, *opnd0, *opnd1, opnd2));
4646 }
4647
SelectShift(Operand & resOpnd,Operand & opnd0,Operand & opnd1,ShiftDirection direct,PrimType primType)4648 void AArch64CGFunc::SelectShift(Operand &resOpnd, Operand &opnd0, Operand &opnd1, ShiftDirection direct,
4649 PrimType primType)
4650 {
4651 Operand::OperandType opnd1Type = opnd1.GetKind();
4652 uint32 dsize = GetPrimTypeBitSize(primType);
4653 bool is64Bits = (dsize == k64BitSize);
4654 Operand *firstOpnd = &LoadIntoRegister(opnd0, primType);
4655
4656 MOperator mopShift;
4657 if ((opnd1Type == Operand::kOpdImmediate) || (opnd1Type == Operand::kOpdOffset)) {
4658 ImmOperand *immOpnd1 = static_cast<ImmOperand *>(&opnd1);
4659 const int64 kVal = immOpnd1->GetValue();
4660 const uint32 kShiftamt = is64Bits ? kHighestBitOf64Bits : kHighestBitOf32Bits;
4661 if (kVal == 0) {
4662 SelectCopy(resOpnd, primType, *firstOpnd, primType);
4663 return;
4664 }
4665 /* e.g. a >> -1 */
4666 if ((kVal < 0) || (kVal > kShiftamt)) {
4667 SelectShift(resOpnd, *firstOpnd, SelectCopy(opnd1, primType, primType), direct, primType);
4668 return;
4669 }
4670 switch (direct) {
4671 case kShiftLeft:
4672 mopShift = is64Bits ? MOP_xlslrri6 : MOP_wlslrri5;
4673 break;
4674 case kShiftAright:
4675 mopShift = is64Bits ? MOP_xasrrri6 : MOP_wasrrri5;
4676 break;
4677 case kShiftLright:
4678 mopShift = is64Bits ? MOP_xlsrrri6 : MOP_wlsrrri5;
4679 break;
4680 }
4681 } else if (opnd1Type != Operand::kOpdRegister) {
4682 SelectShift(resOpnd, *firstOpnd, SelectCopy(opnd1, primType, primType), direct, primType);
4683 return;
4684 } else {
4685 switch (direct) {
4686 case kShiftLeft:
4687 mopShift = is64Bits ? MOP_xlslrrr : MOP_wlslrrr;
4688 break;
4689 case kShiftAright:
4690 mopShift = is64Bits ? MOP_xasrrrr : MOP_wasrrrr;
4691 break;
4692 case kShiftLright:
4693 mopShift = is64Bits ? MOP_xlsrrrr : MOP_wlsrrrr;
4694 break;
4695 }
4696 }
4697
4698 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopShift, resOpnd, *firstOpnd, opnd1));
4699 }
4700
SelectAbsSub(Insn & lastInsn,const UnaryNode & node,Operand & newOpnd0)4701 Operand *AArch64CGFunc::SelectAbsSub(Insn &lastInsn, const UnaryNode &node, Operand &newOpnd0)
4702 {
4703 PrimType dtyp = node.GetPrimType();
4704 bool is64Bits = (GetPrimTypeBitSize(dtyp) == k64BitSize);
4705 /* promoted type */
4706 PrimType primType = is64Bits ? (PTY_i64) : (PTY_i32);
4707 RegOperand &resOpnd = CreateRegisterOperandOfType(primType);
4708 uint32 mopCsneg = is64Bits ? MOP_xcnegrrrc : MOP_wcnegrrrc;
4709 /* ABS requires the operand be interpreted as a signed integer */
4710 CondOperand &condOpnd = GetCondOperand(CC_MI);
4711 MOperator newMop = AArch64isa::GetMopSub2Subs(lastInsn);
4712 Operand &rflag = GetOrCreateRflag();
4713 std::vector<Operand *> opndVec;
4714 opndVec.push_back(&rflag);
4715 for (uint32 i = 0; i < lastInsn.GetOperandSize(); i++) {
4716 opndVec.push_back(&lastInsn.GetOperand(i));
4717 }
4718 Insn *subsInsn = &GetInsnBuilder()->BuildInsn(newMop, opndVec);
4719 GetCurBB()->ReplaceInsn(lastInsn, *subsInsn);
4720 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopCsneg, resOpnd, newOpnd0, condOpnd, rflag));
4721 return &resOpnd;
4722 }
4723
SelectAbs(UnaryNode & node,Operand & opnd0)4724 Operand *AArch64CGFunc::SelectAbs(UnaryNode &node, Operand &opnd0)
4725 {
4726 PrimType dtyp = node.GetPrimType();
4727 if (IsPrimitiveVector(dtyp)) {
4728 return SelectVectorAbs(dtyp, &opnd0);
4729 } else if (IsPrimitiveFloat(dtyp)) {
4730 CHECK_FATAL(GetPrimTypeBitSize(dtyp) >= k32BitSize, "We don't support hanf-word FP operands yet");
4731 bool is64Bits = (GetPrimTypeBitSize(dtyp) == k64BitSize);
4732 Operand &newOpnd0 = LoadIntoRegister(opnd0, dtyp);
4733 RegOperand &resOpnd = CreateRegisterOperandOfType(dtyp);
4734 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(is64Bits ? MOP_dabsrr : MOP_sabsrr, resOpnd, newOpnd0));
4735 return &resOpnd;
4736 } else {
4737 bool is64Bits = (GetPrimTypeBitSize(dtyp) == k64BitSize);
4738 /* promoted type */
4739 PrimType primType = is64Bits ? (PTY_i64) : (PTY_i32);
4740 Operand &newOpnd0 = LoadIntoRegister(opnd0, primType);
4741 Insn *lastInsn = GetCurBB()->GetLastMachineInsn();
4742 if (lastInsn != nullptr && AArch64isa::IsSub(*lastInsn)) {
4743 Operand &dest = lastInsn->GetOperand(kInsnFirstOpnd);
4744 Operand &opd1 = lastInsn->GetOperand(kInsnSecondOpnd);
4745 Operand &opd2 = lastInsn->GetOperand(kInsnThirdOpnd);
4746 regno_t absReg = static_cast<RegOperand &>(newOpnd0).GetRegisterNumber();
4747 if ((dest.IsRegister() && static_cast<RegOperand &>(dest).GetRegisterNumber() == absReg) ||
4748 (opd1.IsRegister() && static_cast<RegOperand &>(opd1).GetRegisterNumber() == absReg) ||
4749 (opd2.IsRegister() && static_cast<RegOperand &>(opd2).GetRegisterNumber() == absReg)) {
4750 return SelectAbsSub(*lastInsn, node, newOpnd0);
4751 }
4752 }
4753 RegOperand &resOpnd = CreateRegisterOperandOfType(primType);
4754 SelectAArch64Cmp(newOpnd0, CreateImmOperand(0, is64Bits ? PTY_u64 : PTY_u32, false), true,
4755 GetPrimTypeBitSize(dtyp));
4756 uint32 mopCsneg = is64Bits ? MOP_xcsnegrrrc : MOP_wcsnegrrrc;
4757 /* ABS requires the operand be interpreted as a signed integer */
4758 CondOperand &condOpnd = GetCondOperand(CC_GE);
4759 Operand &rflag = GetOrCreateRflag();
4760 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopCsneg, resOpnd, newOpnd0, newOpnd0, condOpnd, rflag));
4761 return &resOpnd;
4762 }
4763 }
4764
SelectBnot(UnaryNode & node,Operand & opnd0,const BaseNode & parent)4765 Operand *AArch64CGFunc::SelectBnot(UnaryNode &node, Operand &opnd0, const BaseNode &parent)
4766 {
4767 PrimType dtype = node.GetPrimType();
4768 DEBUG_ASSERT(IsPrimitiveInteger(dtype) || IsPrimitiveVectorInteger(dtype), "bnot expect integer or NYI");
4769 uint32 bitSize = GetPrimTypeBitSize(dtype);
4770 bool is64Bits = (bitSize == k64BitSize);
4771 bool isSigned = IsSignedInteger(dtype);
4772 RegOperand *resOpnd = nullptr;
4773 if (!IsPrimitiveVector(dtype)) {
4774 /* promoted type */
4775 PrimType primType = is64Bits ? (isSigned ? PTY_i64 : PTY_u64) : (isSigned ? PTY_i32 : PTY_u32);
4776 resOpnd = &GetOrCreateResOperand(parent, primType);
4777
4778 Operand &newOpnd0 = LoadIntoRegister(opnd0, primType);
4779
4780 uint32 mopBnot = is64Bits ? MOP_xnotrr : MOP_wnotrr;
4781 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopBnot, *resOpnd, newOpnd0));
4782 /* generate and resOpnd, resOpnd, 0x1/0xFF/0xFFFF for PTY_u1/PTY_u8/PTY_u16 */
4783 int64 immValue = 0;
4784 if (bitSize == k1BitSize) {
4785 immValue = 1;
4786 } else if (bitSize == k8BitSize) {
4787 immValue = 0xFF;
4788 } else if (bitSize == k16BitSize) {
4789 immValue = 0xFFFF;
4790 }
4791 if (immValue != 0) {
4792 ImmOperand &imm = CreateImmOperand(PTY_u32, immValue);
4793 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wandrri12, *resOpnd, *resOpnd, imm));
4794 }
4795 } else {
4796 /* vector operand */
4797 resOpnd = SelectVectorNot(dtype, &opnd0);
4798 }
4799 return resOpnd;
4800 }
4801
SelectBswap(IntrinsicopNode & node,Operand & opnd0,const BaseNode & parent)4802 Operand *AArch64CGFunc::SelectBswap(IntrinsicopNode &node, Operand &opnd0, const BaseNode &parent)
4803 {
4804 PrimType dtype = node.GetPrimType();
4805 auto bitWidth = (GetPrimTypeBitSize(dtype));
4806 RegOperand *resOpnd = nullptr;
4807 resOpnd = &GetOrCreateResOperand(parent, dtype);
4808 Operand &newOpnd0 = LoadIntoRegister(opnd0, dtype);
4809 uint32 mopBswap = bitWidth == 64 ? MOP_xrevrr : (bitWidth == 32 ? MOP_wrevrr : MOP_wrevrr16);
4810 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopBswap, *resOpnd, newOpnd0));
4811 return resOpnd;
4812 }
4813
SelectRegularBitFieldLoad(ExtractbitsNode & node,const BaseNode & parent)4814 Operand *AArch64CGFunc::SelectRegularBitFieldLoad(ExtractbitsNode &node, const BaseNode &parent)
4815 {
4816 PrimType dtype = node.GetPrimType();
4817 bool isSigned = IsSignedInteger(dtype);
4818 uint8 bitOffset = node.GetBitsOffset();
4819 uint8 bitSize = node.GetBitsSize();
4820 bool is64Bits = (GetPrimTypeBitSize(dtype) == k64BitSize);
4821 CHECK_FATAL(!is64Bits, "dest opnd should not be 64bit");
4822 PrimType destType = GetIntegerPrimTypeBySizeAndSign(bitSize, isSigned);
4823 Operand *result =
4824 SelectIread(parent, *static_cast<IreadNode *>(node.Opnd(0)), static_cast<int>(bitOffset / k8BitSize), destType);
4825 return result;
4826 }
4827
SelectExtractbits(ExtractbitsNode & node,Operand & srcOpnd,const BaseNode & parent)4828 Operand *AArch64CGFunc::SelectExtractbits(ExtractbitsNode &node, Operand &srcOpnd, const BaseNode &parent)
4829 {
4830 uint8 bitOffset = node.GetBitsOffset();
4831 uint8 bitSize = node.GetBitsSize();
4832 RegOperand *srcVecRegOperand = static_cast<RegOperand *>(&srcOpnd);
4833 if (srcVecRegOperand && srcVecRegOperand->IsRegister() && (srcVecRegOperand->GetSize() == k128BitSize)) {
4834 if ((bitSize == k8BitSize || bitSize == k16BitSize || bitSize == k32BitSize || bitSize == k64BitSize) &&
4835 (bitOffset % bitSize) == k0BitSize) {
4836 uint32 lane = bitOffset / bitSize;
4837 PrimType srcVecPtype;
4838 if (bitSize == k64BitSize) {
4839 srcVecPtype = PTY_v2u64;
4840 } else if (bitSize == k32BitSize) {
4841 srcVecPtype = PTY_v4u32;
4842 } else if (bitSize == k16BitSize) {
4843 srcVecPtype = PTY_v8u16;
4844 } else {
4845 srcVecPtype = PTY_v16u8;
4846 }
4847 RegOperand *resRegOperand =
4848 SelectVectorGetElement(node.GetPrimType(), &srcOpnd, srcVecPtype, static_cast<int32>(lane));
4849 return resRegOperand;
4850 } else {
4851 CHECK_FATAL(false, "NYI");
4852 }
4853 }
4854 PrimType dtype = node.GetPrimType();
4855 RegOperand &resOpnd = GetOrCreateResOperand(parent, dtype);
4856 bool isSigned =
4857 (node.GetOpCode() == OP_sext) ? true : (node.GetOpCode() == OP_zext) ? false : IsSignedInteger(dtype);
4858 bool is64Bits = (GetPrimTypeBitSize(dtype) == k64BitSize);
4859 uint32 immWidth = is64Bits ? kMaxImmVal13Bits : kMaxImmVal12Bits;
4860 Operand &opnd0 = LoadIntoRegister(srcOpnd, dtype);
4861 if (bitOffset == 0) {
4862 if (!isSigned && (bitSize < immWidth)) {
4863 SelectBand(resOpnd, opnd0,
4864 CreateImmOperand(static_cast<int64>((static_cast<uint64>(1) << bitSize) - 1), immWidth, false),
4865 dtype);
4866 return &resOpnd;
4867 } else {
4868 MOperator mOp = MOP_undef;
4869 if (bitSize == k8BitSize) {
4870 mOp = is64Bits ? (isSigned ? MOP_xsxtb64 : MOP_undef)
4871 : (isSigned ? MOP_xsxtb32 : (opnd0.GetSize() == k32BitSize ? MOP_xuxtb32 : MOP_undef));
4872 } else if (bitSize == k16BitSize) {
4873 mOp = is64Bits ? (isSigned ? MOP_xsxth64 : MOP_undef)
4874 : (isSigned ? MOP_xsxth32 : (opnd0.GetSize() == k32BitSize ? MOP_xuxth32 : MOP_undef));
4875 } else if (bitSize == k32BitSize) {
4876 mOp = is64Bits ? (isSigned ? MOP_xsxtw64 : MOP_xuxtw64) : MOP_wmovrr;
4877 }
4878 if (mOp != MOP_undef) {
4879 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opnd0));
4880 return &resOpnd;
4881 }
4882 }
4883 }
4884 uint32 mopBfx =
4885 is64Bits ? (isSigned ? MOP_xsbfxrri6i6 : MOP_xubfxrri6i6) : (isSigned ? MOP_wsbfxrri5i5 : MOP_wubfxrri5i5);
4886 ImmOperand &immOpnd1 = CreateImmOperand(bitOffset, k8BitSize, false);
4887 ImmOperand &immOpnd2 = CreateImmOperand(bitSize, k8BitSize, false);
4888 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopBfx, resOpnd, opnd0, immOpnd1, immOpnd2));
4889 return &resOpnd;
4890 }
4891
4892 /*
4893 * operand fits in MOVK if
4894 * is64Bits && bitOffset == 0, 16, 32, 48 && bSize == 16
4895 * or is32Bits && bitOffset == 0, 16 && bSize == 16
4896 * imm range of aarch64-movk is [0 - 65536] imm16
4897 */
IsMoveWideKeepable(int64 offsetVal,uint32 bitOffset,uint32 bitSize,bool is64Bits)4898 inline bool IsMoveWideKeepable(int64 offsetVal, uint32 bitOffset, uint32 bitSize, bool is64Bits)
4899 {
4900 DEBUG_ASSERT(is64Bits || (bitOffset < k32BitSize), "");
4901 bool isOutOfRange = offsetVal < 0;
4902 if (!isOutOfRange) {
4903 isOutOfRange = (static_cast<unsigned long int>(offsetVal) >> k16BitSize) > 0;
4904 }
4905 if (isOutOfRange) {
4906 return false;
4907 }
4908 return ((bitOffset == k0BitSize || bitOffset == k16BitSize) ||
4909 (is64Bits && (bitOffset == k32BitSize || bitOffset == k48BitSize))) &&
4910 bitSize == k16BitSize;
4911 }
4912
4913 /* we use the fact that A ^ B ^ A == B, A ^ 0 = A */
SelectDepositBits(DepositbitsNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)4914 Operand *AArch64CGFunc::SelectDepositBits(DepositbitsNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
4915 {
4916 uint32 bitOffset = node.GetBitsOffset();
4917 uint32 bitSize = node.GetBitsSize();
4918 PrimType regType = node.GetPrimType();
4919 bool is64Bits = GetPrimTypeBitSize(regType) == k64BitSize;
4920 /*
4921 * if operand 1 is immediate and fits in MOVK, use it
4922 * MOVK Wd, #imm{, LSL #shift} ; 32-bit general registers
4923 * MOVK Xd, #imm{, LSL #shift} ; 64-bit general registers
4924 */
4925 if (opnd1.IsIntImmediate() &&
4926 IsMoveWideKeepable(static_cast<ImmOperand &>(opnd1).GetValue(), bitOffset, bitSize, is64Bits)) {
4927 RegOperand &resOpnd = GetOrCreateResOperand(parent, regType);
4928 SelectCopy(resOpnd, regType, opnd0, regType);
4929 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn((is64Bits ? MOP_xmovkri16 : MOP_wmovkri16), resOpnd, opnd1,
4930 *GetLogicalShiftLeftOperand(bitOffset, is64Bits)));
4931 return &resOpnd;
4932 } else {
4933 Operand &movOpnd = LoadIntoRegister(opnd1, regType);
4934 uint32 mopBfi = is64Bits ? MOP_xbfirri6i6 : MOP_wbfirri5i5;
4935 ImmOperand &immOpnd1 = CreateImmOperand(bitOffset, k8BitSize, false);
4936 ImmOperand &immOpnd2 = CreateImmOperand(bitSize, k8BitSize, false);
4937 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopBfi, opnd0, movOpnd, immOpnd1, immOpnd2));
4938 return &opnd0;
4939 }
4940 }
4941
SelectLnot(UnaryNode & node,Operand & srcOpnd,const BaseNode & parent)4942 Operand *AArch64CGFunc::SelectLnot(UnaryNode &node, Operand &srcOpnd, const BaseNode &parent)
4943 {
4944 PrimType dtype = node.GetPrimType();
4945 RegOperand &resOpnd = GetOrCreateResOperand(parent, dtype);
4946 bool is64Bits = (GetPrimTypeBitSize(dtype) == k64BitSize);
4947 Operand &opnd0 = LoadIntoRegister(srcOpnd, dtype);
4948 SelectAArch64Cmp(opnd0, CreateImmOperand(0, is64Bits ? PTY_u64 : PTY_u32, false), true, GetPrimTypeBitSize(dtype));
4949 SelectAArch64CSet(resOpnd, GetCondOperand(CC_EQ), is64Bits);
4950 return &resOpnd;
4951 }
4952
SelectNeg(UnaryNode & node,Operand & opnd0,const BaseNode & parent)4953 Operand *AArch64CGFunc::SelectNeg(UnaryNode &node, Operand &opnd0, const BaseNode &parent)
4954 {
4955 PrimType dtype = node.GetPrimType();
4956 bool is64Bits = (GetPrimTypeBitSize(dtype) == k64BitSize);
4957 RegOperand *resOpnd = nullptr;
4958 if (!IsPrimitiveVector(dtype)) {
4959 PrimType primType;
4960 if (IsPrimitiveFloat(dtype)) {
4961 primType = dtype;
4962 } else {
4963 primType = is64Bits ? (PTY_i64) : (PTY_i32); /* promoted type */
4964 }
4965 resOpnd = &GetOrCreateResOperand(parent, primType);
4966 SelectNeg(*resOpnd, opnd0, primType);
4967 } else {
4968 /* vector operand */
4969 resOpnd = SelectVectorNeg(dtype, &opnd0);
4970 }
4971 return resOpnd;
4972 }
4973
SelectNeg(Operand & dest,Operand & srcOpnd,PrimType primType)4974 void AArch64CGFunc::SelectNeg(Operand &dest, Operand &srcOpnd, PrimType primType)
4975 {
4976 Operand &opnd0 = LoadIntoRegister(srcOpnd, primType);
4977 bool is64Bits = (GetPrimTypeBitSize(primType) == k64BitSize);
4978 MOperator mOp;
4979 if (IsPrimitiveFloat(primType)) {
4980 mOp = is64Bits ? MOP_xfnegrr : MOP_wfnegrr;
4981 } else {
4982 mOp = is64Bits ? MOP_xinegrr : MOP_winegrr;
4983 }
4984 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, dest, opnd0));
4985 }
4986
SelectMvn(Operand & dest,Operand & src,PrimType primType)4987 void AArch64CGFunc::SelectMvn(Operand &dest, Operand &src, PrimType primType)
4988 {
4989 Operand &opnd0 = LoadIntoRegister(src, primType);
4990 bool is64Bits = (GetPrimTypeBitSize(primType) == k64BitSize);
4991 MOperator mOp;
4992 DEBUG_ASSERT(!IsPrimitiveFloat(primType), "Instruction 'mvn' do not have float version.");
4993 mOp = is64Bits ? MOP_xnotrr : MOP_wnotrr;
4994 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, dest, opnd0));
4995 }
4996
SelectRecip(UnaryNode & node,Operand & src,const BaseNode & parent)4997 Operand *AArch64CGFunc::SelectRecip(UnaryNode &node, Operand &src, const BaseNode &parent)
4998 {
4999 /*
5000 * fconsts s15, #112
5001 * fdivs s0, s15, s0
5002 */
5003 PrimType dtype = node.GetPrimType();
5004 if (!IsPrimitiveFloat(dtype)) {
5005 DEBUG_ASSERT(false, "should be float type");
5006 return nullptr;
5007 }
5008 Operand &opnd0 = LoadIntoRegister(src, dtype);
5009 RegOperand &resOpnd = GetOrCreateResOperand(parent, dtype);
5010 Operand *one = nullptr;
5011 if (GetPrimTypeBitSize(dtype) == k64BitSize) {
5012 MIRDoubleConst *c = memPool->New<MIRDoubleConst>(1.0, *GlobalTables::GetTypeTable().GetTypeTable().at(PTY_f64));
5013 one = SelectDoubleConst(*c, node);
5014 } else if (GetPrimTypeBitSize(dtype) == k32BitSize) {
5015 MIRFloatConst *c = memPool->New<MIRFloatConst>(1.0f, *GlobalTables::GetTypeTable().GetTypeTable().at(PTY_f32));
5016 one = SelectFloatConst(*c, node);
5017 } else {
5018 CHECK_FATAL(false, "we don't support half-precision fp operations yet");
5019 }
5020 SelectDiv(resOpnd, *one, opnd0, dtype);
5021 return &resOpnd;
5022 }
5023
SelectSqrt(UnaryNode & node,Operand & src,const BaseNode & parent)5024 Operand *AArch64CGFunc::SelectSqrt(UnaryNode &node, Operand &src, const BaseNode &parent)
5025 {
5026 /*
5027 * gcc generates code like below for better accurate
5028 * fsqrts s15, s0
5029 * fcmps s15, s15
5030 * fmstat
5031 * beq .L4
5032 * push {r3, lr}
5033 * bl sqrtf
5034 * pop {r3, pc}
5035 * .L4:
5036 * fcpys s0, s15
5037 * bx lr
5038 */
5039 PrimType dtype = node.GetPrimType();
5040 if (!IsPrimitiveFloat(dtype)) {
5041 DEBUG_ASSERT(false, "should be float type");
5042 return nullptr;
5043 }
5044 bool is64Bits = (GetPrimTypeBitSize(dtype) == k64BitSize);
5045 Operand &opnd0 = LoadIntoRegister(src, dtype);
5046 RegOperand &resOpnd = GetOrCreateResOperand(parent, dtype);
5047 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(is64Bits ? MOP_vsqrtd : MOP_vsqrts, resOpnd, opnd0));
5048 return &resOpnd;
5049 }
5050
SelectCvtFloat2Int(Operand & resOpnd,Operand & srcOpnd,PrimType itype,PrimType ftype)5051 void AArch64CGFunc::SelectCvtFloat2Int(Operand &resOpnd, Operand &srcOpnd, PrimType itype, PrimType ftype)
5052 {
5053 bool is64BitsFloat = (ftype == PTY_f64);
5054 MOperator mOp = 0;
5055
5056 DEBUG_ASSERT(((ftype == PTY_f64) || (ftype == PTY_f32)), "wrong from type");
5057 Operand &opnd0 = LoadIntoRegister(srcOpnd, ftype);
5058 switch (itype) {
5059 case PTY_i32:
5060 mOp = !is64BitsFloat ? MOP_vcvtrf : MOP_vcvtrd;
5061 break;
5062 case PTY_u32:
5063 case PTY_a32:
5064 mOp = !is64BitsFloat ? MOP_vcvturf : MOP_vcvturd;
5065 break;
5066 case PTY_i64:
5067 mOp = !is64BitsFloat ? MOP_xvcvtrf : MOP_xvcvtrd;
5068 break;
5069 case PTY_u64:
5070 case PTY_a64:
5071 mOp = !is64BitsFloat ? MOP_xvcvturf : MOP_xvcvturd;
5072 break;
5073 default:
5074 CHECK_FATAL(false, "unexpected type");
5075 }
5076 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opnd0));
5077 }
5078
SelectCvtInt2Float(Operand & resOpnd,Operand & origOpnd0,PrimType toType,PrimType fromType)5079 void AArch64CGFunc::SelectCvtInt2Float(Operand &resOpnd, Operand &origOpnd0, PrimType toType, PrimType fromType)
5080 {
5081 DEBUG_ASSERT((toType == PTY_f32) || (toType == PTY_f64), "unexpected type");
5082 bool is64BitsFloat = (toType == PTY_f64);
5083 MOperator mOp = 0;
5084 uint32 fsize = GetPrimTypeBitSize(fromType);
5085
5086 PrimType itype = (GetPrimTypeBitSize(fromType) == k64BitSize) ? (IsSignedInteger(fromType) ? PTY_i64 : PTY_u64)
5087 : (IsSignedInteger(fromType) ? PTY_i32 : PTY_u32);
5088
5089 Operand *opnd0 = &LoadIntoRegister(origOpnd0, itype);
5090
5091 /* need extension before cvt */
5092 DEBUG_ASSERT(opnd0->IsRegister(), "opnd should be a register operand");
5093 Operand *srcOpnd = opnd0;
5094 if (IsSignedInteger(fromType) && (fsize < k32BitSize)) {
5095 srcOpnd = &CreateRegisterOperandOfType(itype);
5096 mOp = (fsize == k8BitSize) ? MOP_xsxtb32 : MOP_xsxth32;
5097 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, *srcOpnd, *opnd0));
5098 }
5099
5100 switch (itype) {
5101 case PTY_i32:
5102 mOp = !is64BitsFloat ? MOP_vcvtfr : MOP_vcvtdr;
5103 break;
5104 case PTY_u32:
5105 mOp = !is64BitsFloat ? MOP_vcvtufr : MOP_vcvtudr;
5106 break;
5107 case PTY_i64:
5108 mOp = !is64BitsFloat ? MOP_xvcvtfr : MOP_xvcvtdr;
5109 break;
5110 case PTY_u64:
5111 mOp = !is64BitsFloat ? MOP_xvcvtufr : MOP_xvcvtudr;
5112 break;
5113 default:
5114 CHECK_FATAL(false, "unexpected type");
5115 }
5116 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, *srcOpnd));
5117 }
5118
SelectIntrinsicOpWithOneParam(IntrinsicopNode & intrnNode,std::string name)5119 Operand *AArch64CGFunc::SelectIntrinsicOpWithOneParam(IntrinsicopNode &intrnNode, std::string name)
5120 {
5121 BaseNode *argexpr = intrnNode.Opnd(0);
5122 PrimType ptype = argexpr->GetPrimType();
5123 Operand *opnd = HandleExpr(intrnNode, *argexpr);
5124 if (intrnNode.GetIntrinsic() == INTRN_C_ffs) {
5125 DEBUG_ASSERT(intrnNode.GetPrimType() == PTY_i32, "Unexpect Size");
5126 return SelectAArch64ffs(*opnd, ptype);
5127 }
5128 if (opnd->IsMemoryAccessOperand()) {
5129 RegOperand &ldDest = CreateRegisterOperandOfType(ptype);
5130 Insn &insn = GetInsnBuilder()->BuildInsn(PickLdInsn(GetPrimTypeBitSize(ptype), ptype), ldDest, *opnd);
5131 GetCurBB()->AppendInsn(insn);
5132 opnd = &ldDest;
5133 }
5134 std::vector<Operand *> opndVec;
5135 RegOperand *dst = &CreateRegisterOperandOfType(ptype);
5136 opndVec.push_back(dst); /* result */
5137 opndVec.push_back(opnd); /* param 0 */
5138 SelectLibCall(name, opndVec, ptype, ptype);
5139
5140 return dst;
5141 }
5142
SelectIntrinsicOpWithNParams(IntrinsicopNode & intrnNode,PrimType retType,const std::string & name)5143 Operand *AArch64CGFunc::SelectIntrinsicOpWithNParams(IntrinsicopNode &intrnNode, PrimType retType,
5144 const std::string &name)
5145 {
5146 MapleVector<BaseNode *> argNodes = intrnNode.GetNopnd();
5147 std::vector<Operand *> opndVec;
5148 std::vector<PrimType> opndTypes;
5149 RegOperand *retOpnd = &CreateRegisterOperandOfType(retType);
5150 opndVec.push_back(retOpnd);
5151 opndTypes.push_back(retType);
5152
5153 for (BaseNode *argexpr : argNodes) {
5154 PrimType ptype = argexpr->GetPrimType();
5155 Operand *opnd = HandleExpr(intrnNode, *argexpr);
5156 if (opnd->IsMemoryAccessOperand()) {
5157 RegOperand &ldDest = CreateRegisterOperandOfType(ptype);
5158 Insn &insn = GetInsnBuilder()->BuildInsn(PickLdInsn(GetPrimTypeBitSize(ptype), ptype), ldDest, *opnd);
5159 GetCurBB()->AppendInsn(insn);
5160 opnd = &ldDest;
5161 }
5162 opndVec.push_back(opnd);
5163 opndTypes.push_back(ptype);
5164 }
5165 SelectLibCallNArg(name, opndVec, opndTypes, retType, false);
5166
5167 return retOpnd;
5168 }
5169
5170 /* According to gcc.target/aarch64/ffs.c */
SelectAArch64ffs(Operand & argOpnd,PrimType argType)5171 Operand *AArch64CGFunc::SelectAArch64ffs(Operand &argOpnd, PrimType argType)
5172 {
5173 RegOperand &destOpnd = LoadIntoRegister(argOpnd, argType);
5174 uint32 argSize = GetPrimTypeBitSize(argType);
5175 DEBUG_ASSERT((argSize == k64BitSize || argSize == k32BitSize), "Unexpect arg type");
5176 /* cmp */
5177 ImmOperand &zeroOpnd = CreateImmOperand(0, argSize, false);
5178 Operand &rflag = GetOrCreateRflag();
5179 GetCurBB()->AppendInsn(
5180 GetInsnBuilder()->BuildInsn(argSize == k64BitSize ? MOP_xcmpri : MOP_wcmpri, rflag, destOpnd, zeroOpnd));
5181 /* rbit */
5182 RegOperand *tempResReg = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, GetPrimTypeSize(argType)));
5183 GetCurBB()->AppendInsn(
5184 GetInsnBuilder()->BuildInsn(argSize == k64BitSize ? MOP_xrbit : MOP_wrbit, *tempResReg, destOpnd));
5185 /* clz */
5186 GetCurBB()->AppendInsn(
5187 GetInsnBuilder()->BuildInsn(argSize == k64BitSize ? MOP_xclz : MOP_wclz, *tempResReg, *tempResReg));
5188 /* csincc */
5189 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(argSize == k64BitSize ? MOP_xcsincrrrc : MOP_wcsincrrrc,
5190 *tempResReg, GetZeroOpnd(k32BitSize), *tempResReg,
5191 GetCondOperand(CC_EQ), rflag));
5192 return tempResReg;
5193 }
5194
SelectRoundLibCall(RoundType roundType,const TypeCvtNode & node,Operand & opnd0)5195 Operand *AArch64CGFunc::SelectRoundLibCall(RoundType roundType, const TypeCvtNode &node, Operand &opnd0)
5196 {
5197 PrimType ftype = node.FromType();
5198 PrimType rtype = node.GetPrimType();
5199 bool is64Bits = (ftype == PTY_f64);
5200 std::vector<Operand *> opndVec;
5201 RegOperand *resOpnd;
5202 if (is64Bits) {
5203 resOpnd = &GetOrCreatePhysicalRegisterOperand(D0, k64BitSize, kRegTyFloat);
5204 } else {
5205 resOpnd = &GetOrCreatePhysicalRegisterOperand(S0, k32BitSize, kRegTyFloat);
5206 }
5207 opndVec.push_back(resOpnd);
5208 RegOperand ®Opnd0 = LoadIntoRegister(opnd0, ftype);
5209 opndVec.push_back(®Opnd0);
5210 std::string libName;
5211 if (roundType == kCeil) {
5212 libName.assign(is64Bits ? "ceil" : "ceilf");
5213 } else if (roundType == kFloor) {
5214 libName.assign(is64Bits ? "floor" : "floorf");
5215 } else {
5216 libName.assign(is64Bits ? "round" : "roundf");
5217 }
5218 SelectLibCall(libName, opndVec, ftype, rtype);
5219
5220 return resOpnd;
5221 }
5222
SelectRoundOperator(RoundType roundType,const TypeCvtNode & node,Operand & opnd0,const BaseNode & parent)5223 Operand *AArch64CGFunc::SelectRoundOperator(RoundType roundType, const TypeCvtNode &node, Operand &opnd0,
5224 const BaseNode &parent)
5225 {
5226 PrimType itype = node.GetPrimType();
5227 if ((mirModule.GetSrcLang() == kSrcLangC) && ((itype == PTY_f64) || (itype == PTY_f32))) {
5228 SelectRoundLibCall(roundType, node, opnd0);
5229 }
5230 PrimType ftype = node.FromType();
5231 DEBUG_ASSERT(((ftype == PTY_f64) || (ftype == PTY_f32)), "wrong float type");
5232 bool is64Bits = (ftype == PTY_f64);
5233 bool isFloat = (ftype == PTY_f64) || (ftype == PTY_f32);
5234 RegOperand &resOpnd = GetOrCreateResOperand(parent, itype);
5235 RegOperand ®Opnd0 = LoadIntoRegister(opnd0, ftype);
5236 MOperator mop = MOP_undef;
5237 if (roundType == kCeil) {
5238 if (isFloat) {
5239 mop = is64Bits ? MOP_dfrintprr : MOP_sfrintprr;
5240 } else {
5241 mop = is64Bits ? MOP_xvcvtps : MOP_vcvtps;
5242 }
5243 } else if (roundType == kFloor) {
5244 if (isFloat) {
5245 mop = is64Bits ? MOP_dfrintmrr : MOP_sfrintmrr;
5246 } else {
5247 mop = is64Bits ? MOP_xvcvtms : MOP_vcvtms;
5248 }
5249 } else if (roundType == kTrunc) {
5250 if (isFloat) {
5251 mop = is64Bits ? MOP_dfrintzrr : MOP_sfrintzrr;
5252 } else {
5253 CHECK_FATAL(false, "not support here!");
5254 }
5255 } else {
5256 CHECK_FATAL(!isFloat, "not support float here!");
5257 mop = is64Bits ? MOP_xvcvtas : MOP_vcvtas;
5258 }
5259 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mop, resOpnd, regOpnd0));
5260 return &resOpnd;
5261 }
5262
SelectCeil(TypeCvtNode & node,Operand & opnd0,const BaseNode & parent)5263 Operand *AArch64CGFunc::SelectCeil(TypeCvtNode &node, Operand &opnd0, const BaseNode &parent)
5264 {
5265 return SelectRoundOperator(kCeil, node, opnd0, parent);
5266 }
5267
5268 /* float to int floor */
SelectFloor(TypeCvtNode & node,Operand & opnd0,const BaseNode & parent)5269 Operand *AArch64CGFunc::SelectFloor(TypeCvtNode &node, Operand &opnd0, const BaseNode &parent)
5270 {
5271 return SelectRoundOperator(kFloor, node, opnd0, parent);
5272 }
5273
SelectRound(TypeCvtNode & node,Operand & opnd0,const BaseNode & parent)5274 Operand *AArch64CGFunc::SelectRound(TypeCvtNode &node, Operand &opnd0, const BaseNode &parent)
5275 {
5276 return SelectRoundOperator(kRound, node, opnd0, parent);
5277 }
5278
LIsPrimitivePointer(PrimType ptype)5279 static bool LIsPrimitivePointer(PrimType ptype)
5280 {
5281 return ((ptype >= PTY_ptr) && (ptype <= PTY_a64));
5282 }
5283
SelectRetype(TypeCvtNode & node,Operand & opnd0)5284 Operand *AArch64CGFunc::SelectRetype(TypeCvtNode &node, Operand &opnd0)
5285 {
5286 PrimType fromType = node.Opnd(0)->GetPrimType();
5287 PrimType toType = node.GetPrimType();
5288 DEBUG_ASSERT(GetPrimTypeSize(fromType) == GetPrimTypeSize(toType), "retype bit widith doesn' match");
5289 if (LIsPrimitivePointer(fromType) && LIsPrimitivePointer(toType)) {
5290 return &LoadIntoRegister(opnd0, toType);
5291 }
5292 // if source operand is in memory,
5293 // simply read it as a value of 'toType 'into the dest operand and return
5294 if (opnd0.IsMemoryAccessOperand()) {
5295 return &SelectCopy(opnd0, toType, toType);
5296 }
5297
5298 bool isFromInt = IsPrimitiveInteger(fromType);
5299 bool is64Bits = GetPrimTypeBitSize(fromType) == k64BitSize;
5300 bool isImm = false;
5301 Operand *newOpnd0 = &opnd0;
5302 if (opnd0.IsImmediate()) {
5303 // according to aarch64 encoding format, convert int to float expression
5304 ImmOperand *imm = static_cast<ImmOperand *>(&opnd0);
5305 uint64 val = static_cast<uint64>(imm->GetValue());
5306 uint64 canRepreset = is64Bits ? (val & 0xffffffffffff) : (val & 0x7ffff);
5307 uint32 val1 = is64Bits ? (val >> 61) & 0x3 : (val >> 29) & 0x3;
5308 uint32 val2 = is64Bits ? (val >> 54) & 0xff : (val >> 25) & 0x1f;
5309 bool isSame = is64Bits ? ((val2 == 0) || (val2 == 0xff)) : ((val2 == 0) || (val2 == 0x1f));
5310 canRepreset = (canRepreset == 0) && ((val1 & 0x1) ^ ((val1 & 0x2) >> 1)) && isSame;
5311 if (IsPrimitiveInteger(fromType) && IsPrimitiveFloat(toType) && canRepreset) {
5312 uint64 temp1 = is64Bits ? (val >> 63) << 7 : (val >> 31) << 7;
5313 uint64 temp2 = is64Bits ? val >> 48 : val >> 19;
5314 int64 imm8 = (temp2 & 0x7f) | temp1;
5315 newOpnd0 = &CreateImmOperand(imm8, k8BitSize, false, kNotVary, true);
5316 isImm = true;
5317 }
5318 }
5319 if (!isImm) {
5320 bool isSigned = IsSignedInteger(fromType);
5321 PrimType itype = isFromInt ? (is64Bits ? (isSigned ? PTY_i64 : PTY_u64) : (isSigned ? PTY_i32 : PTY_u32))
5322 : (is64Bits ? PTY_f64 : PTY_f32);
5323 newOpnd0 = &LoadIntoRegister(opnd0, itype);
5324 }
5325 if ((IsPrimitiveFloat(fromType) && IsPrimitiveInteger(toType)) ||
5326 (IsPrimitiveFloat(toType) && IsPrimitiveInteger(fromType))) {
5327 MOperator mopFmov = isImm ? (is64Bits ? MOP_xdfmovri : MOP_wsfmovri)
5328 : (isFromInt ? (is64Bits ? MOP_xvmovdr : MOP_xvmovsr)
5329 : (is64Bits ? MOP_xvmovrd : MOP_xvmovrs));
5330 RegOperand *resOpnd = &CreateRegisterOperandOfType(toType);
5331 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mopFmov, *resOpnd, *newOpnd0));
5332 return resOpnd;
5333 }
5334 return newOpnd0;
5335 }
5336
SelectCvtFloat2Float(Operand & resOpnd,Operand & srcOpnd,PrimType fromType,PrimType toType)5337 void AArch64CGFunc::SelectCvtFloat2Float(Operand &resOpnd, Operand &srcOpnd, PrimType fromType, PrimType toType)
5338 {
5339 Operand &opnd0 = LoadIntoRegister(srcOpnd, fromType);
5340 MOperator mOp = 0;
5341 switch (toType) {
5342 case PTY_f32: {
5343 CHECK_FATAL(fromType == PTY_f64, "unexpected cvt from type");
5344 mOp = MOP_xvcvtfd;
5345 break;
5346 }
5347 case PTY_f64: {
5348 CHECK_FATAL(fromType == PTY_f32, "unexpected cvt from type");
5349 mOp = MOP_xvcvtdf;
5350 break;
5351 }
5352 default:
5353 CHECK_FATAL(false, "unexpected cvt to type");
5354 }
5355
5356 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, resOpnd, opnd0));
5357 }
5358
5359 /*
5360 * This should be regarded only as a reference.
5361 *
5362 * C11 specification.
5363 * 6.3.1.3 Signed and unsigned integers
5364 * 1 When a value with integer type is converted to another integer
5365 * type other than _Bool, if the value can be represented by the
5366 * new type, it is unchanged.
5367 * 2 Otherwise, if the new type is unsigned, the value is converted
5368 * by repeatedly adding or subtracting one more than the maximum
5369 * value that can be represented in the new type until the value
5370 * is in the range of the new type.60)
5371 * 3 Otherwise, the new type is signed and the value cannot be
5372 * represented in it; either the result is implementation-defined
5373 * or an implementation-defined signal is raised.
5374 */
SelectCvtInt2Int(const BaseNode * parent,Operand * & resOpnd,Operand * opnd0,PrimType fromType,PrimType toType)5375 void AArch64CGFunc::SelectCvtInt2Int(const BaseNode *parent, Operand *&resOpnd, Operand *opnd0, PrimType fromType,
5376 PrimType toType)
5377 {
5378 uint32 fsize = GetPrimTypeBitSize(fromType);
5379 if (fromType == PTY_i128 || fromType == PTY_u128) {
5380 fsize = k64BitSize;
5381 }
5382 uint32 tsize = GetPrimTypeBitSize(toType);
5383 if (toType == PTY_i128 || toType == PTY_u128) {
5384 tsize = k64BitSize;
5385 }
5386 bool isExpand = tsize > fsize;
5387 bool is64Bit = (tsize == k64BitSize);
5388 if ((parent != nullptr) && opnd0->IsIntImmediate() &&
5389 ((parent->GetOpCode() == OP_band) || (parent->GetOpCode() == OP_bior) || (parent->GetOpCode() == OP_bxor) ||
5390 (parent->GetOpCode() == OP_ashr) || (parent->GetOpCode() == OP_lshr) || (parent->GetOpCode() == OP_shl))) {
5391 ImmOperand *simm = static_cast<ImmOperand *>(opnd0);
5392 DEBUG_ASSERT(simm != nullptr, "simm is nullptr in AArch64CGFunc::SelectCvtInt2Int");
5393 bool isSign = false;
5394 int64 origValue = simm->GetValue();
5395 int64 newValue = origValue;
5396 int64 signValue = 0;
5397 if (!isExpand) {
5398 /* 64--->32 */
5399 if (fsize > tsize) {
5400 if (IsSignedInteger(toType)) {
5401 if (origValue < 0) {
5402 signValue = static_cast<int64>(0xFFFFFFFFFFFFFFFFLL & (1ULL << static_cast<uint32>(tsize)));
5403 }
5404 newValue = static_cast<int64>(
5405 (static_cast<uint64>(origValue) & ((1ULL << static_cast<uint32>(tsize)) - 1u)) |
5406 static_cast<uint64>(signValue));
5407 } else {
5408 newValue = static_cast<uint64>(origValue) & ((1ULL << static_cast<uint32>(tsize)) - 1u);
5409 }
5410 }
5411 }
5412 if (IsSignedInteger(toType)) {
5413 isSign = true;
5414 }
5415 resOpnd = &static_cast<Operand &>(CreateImmOperand(newValue, GetPrimTypeSize(toType) * kBitsPerByte, isSign));
5416 return;
5417 }
5418 if (isExpand) { /* Expansion */
5419 /* if cvt expr's parent is add,and,xor and some other,we can use the imm version */
5420 PrimType primType = ((fsize == k64BitSize) ? (IsSignedInteger(fromType) ? PTY_i64 : PTY_u64)
5421 : (IsSignedInteger(fromType) ? PTY_i32 : PTY_u32));
5422 opnd0 = &LoadIntoRegister(*opnd0, primType);
5423
5424 if (IsSignedInteger(fromType)) {
5425 DEBUG_ASSERT((is64Bit || (fsize == k8BitSize || fsize == k16BitSize)), "incorrect from size");
5426
5427 MOperator mOp =
5428 (is64Bit ? ((fsize == k8BitSize) ? MOP_xsxtb64 : ((fsize == k16BitSize) ? MOP_xsxth64 : MOP_xsxtw64))
5429 : ((fsize == k8BitSize) ? MOP_xsxtb32 : MOP_xsxth32));
5430 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, *resOpnd, *opnd0));
5431 } else {
5432 /* Unsigned */
5433 auto mOp =
5434 (is64Bit ? ((fsize == k8BitSize) ? MOP_xuxtb32 : ((fsize == k16BitSize) ? MOP_xuxth32 : MOP_xuxtw64))
5435 : ((fsize == k8BitSize) ? MOP_xuxtb32 : MOP_xuxth32));
5436 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, *resOpnd, LoadIntoRegister(*opnd0, fromType)));
5437 }
5438 } else { /* Same size or truncate */
5439 #ifdef CNV_OPTIMIZE
5440 /*
5441 * No code needed for aarch64 with same reg.
5442 * Just update regno.
5443 */
5444 RegOperand *reg = static_cast<RegOperand *>(resOpnd);
5445 reg->regNo = static_cast<RegOperand *>(opnd0)->regNo;
5446 #else
5447 /*
5448 * This is not really needed if opnd0 is result from a load.
5449 * Hopefully the FE will get rid of the redundant conversions for loads.
5450 */
5451 PrimType primType = ((fsize == k64BitSize) ? (IsSignedInteger(fromType) ? PTY_i64 : PTY_u64)
5452 : (IsSignedInteger(fromType) ? PTY_i32 : PTY_u32));
5453 opnd0 = &LoadIntoRegister(*opnd0, primType);
5454
5455 if (fsize > tsize) {
5456 if (tsize == k8BitSize) {
5457 MOperator mOp = IsSignedInteger(toType) ? MOP_xsxtb32 : MOP_xuxtb32;
5458 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, *resOpnd, *opnd0));
5459 } else if (tsize == k16BitSize) {
5460 MOperator mOp = IsSignedInteger(toType) ? MOP_xsxth32 : MOP_xuxth32;
5461 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, *resOpnd, *opnd0));
5462 } else {
5463 MOperator mOp = IsSignedInteger(toType) ? MOP_xsbfxrri6i6 : MOP_xubfxrri6i6;
5464 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, *resOpnd, *opnd0,
5465 CreateImmOperand(0, k8BitSize, false),
5466 CreateImmOperand(tsize, k8BitSize, false)));
5467 }
5468 } else {
5469 /* same size, so resOpnd can be set */
5470 if ((IsSignedInteger(fromType) == IsSignedInteger(toType)) ||
5471 (GetPrimTypeSize(toType) >= k4BitSize)) {
5472 resOpnd = opnd0;
5473 } else if (IsUnsignedInteger(toType)) {
5474 MOperator mop;
5475 switch (toType) {
5476 case PTY_u8:
5477 mop = MOP_xuxtb32;
5478 break;
5479 case PTY_u16:
5480 mop = MOP_xuxth32;
5481 break;
5482 default:
5483 CHECK_FATAL(0, "Unhandled unsigned convert");
5484 }
5485 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mop, *resOpnd, *opnd0));
5486 } else {
5487 /* signed target */
5488 uint32 size = GetPrimTypeSize(toType);
5489 MOperator mop;
5490 switch (toType) {
5491 case PTY_i8:
5492 mop = (size > k4BitSize) ? MOP_xsxtb64 : MOP_xsxtb32;
5493 break;
5494 case PTY_i16:
5495 mop = (size > k4BitSize) ? MOP_xsxth64 : MOP_xsxth32;
5496 break;
5497 default:
5498 CHECK_FATAL(0, "Unhandled unsigned convert");
5499 }
5500 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mop, *resOpnd, *opnd0));
5501 }
5502 }
5503 #endif
5504 }
5505 }
5506
SelectCvt(const BaseNode & parent,TypeCvtNode & node,Operand & opnd0)5507 Operand *AArch64CGFunc::SelectCvt(const BaseNode &parent, TypeCvtNode &node, Operand &opnd0)
5508 {
5509 PrimType fromType = node.FromType();
5510 PrimType toType = node.GetPrimType();
5511 if (fromType == toType) {
5512 return &opnd0; /* noop */
5513 }
5514 Operand *resOpnd = &GetOrCreateResOperand(parent, toType);
5515 if (IsPrimitiveFloat(toType) && IsPrimitiveInteger(fromType)) {
5516 SelectCvtInt2Float(*resOpnd, opnd0, toType, fromType);
5517 } else if (IsPrimitiveFloat(fromType) && IsPrimitiveInteger(toType)) {
5518 SelectCvtFloat2Int(*resOpnd, opnd0, toType, fromType);
5519 } else if (IsPrimitiveInteger(fromType) && IsPrimitiveInteger(toType)) {
5520 SelectCvtInt2Int(&parent, resOpnd, &opnd0, fromType, toType);
5521 } else if (IsPrimitiveVector(toType) || IsPrimitiveVector(fromType)) {
5522 CHECK_FATAL(IsPrimitiveVector(toType) && IsPrimitiveVector(fromType), "Invalid vector cvt operands");
5523 SelectVectorCvt(resOpnd, toType, &opnd0, fromType);
5524 } else { /* both are float type */
5525 SelectCvtFloat2Float(*resOpnd, opnd0, fromType, toType);
5526 }
5527 return resOpnd;
5528 }
5529
SelectTrunc(TypeCvtNode & node,Operand & opnd0,const BaseNode & parent)5530 Operand *AArch64CGFunc::SelectTrunc(TypeCvtNode &node, Operand &opnd0, const BaseNode &parent)
5531 {
5532 PrimType ftype = node.FromType();
5533 PrimType nodeType = node.GetPrimType();
5534 bool is64Bits = (GetPrimTypeBitSize(node.GetPrimType()) == k64BitSize);
5535 bool isFloat = (IsPrimitiveFloat(nodeType));
5536 if (isFloat) {
5537 CHECK_FATAL(nodeType == PTY_f32 || nodeType == PTY_f64, "only support f32, f64");
5538 return SelectRoundOperator(kTrunc, node, opnd0, parent);
5539 }
5540 PrimType itype = (is64Bits) ? (IsSignedInteger(node.GetPrimType()) ? PTY_i64 : PTY_u64)
5541 : (IsSignedInteger(node.GetPrimType()) ? PTY_i32 : PTY_u32); /* promoted type */
5542 RegOperand &resOpnd = GetOrCreateResOperand(parent, itype);
5543 SelectCvtFloat2Int(resOpnd, opnd0, itype, ftype);
5544 return &resOpnd;
5545 }
5546
SelectSelect(Operand & resOpnd,Operand & condOpnd,Operand & trueOpnd,Operand & falseOpnd,PrimType dtype,PrimType ctype,bool hasCompare,ConditionCode cc)5547 void AArch64CGFunc::SelectSelect(Operand &resOpnd, Operand &condOpnd, Operand &trueOpnd, Operand &falseOpnd,
5548 PrimType dtype, PrimType ctype, bool hasCompare, ConditionCode cc)
5549 {
5550 DEBUG_ASSERT(&resOpnd != &condOpnd, "resOpnd cannot be the same as condOpnd");
5551 bool isIntType = IsPrimitiveInteger(dtype);
5552 DEBUG_ASSERT((IsPrimitiveInteger(dtype) || IsPrimitiveFloat(dtype)), "unknown type for select");
5553 // making condOpnd and cmpInsn closer will provide more opportunity for opt
5554 Operand &newTrueOpnd = LoadIntoRegister(trueOpnd, dtype);
5555 Operand &newFalseOpnd = LoadIntoRegister(falseOpnd, dtype);
5556 Operand &newCondOpnd = LoadIntoRegister(condOpnd, ctype);
5557 if (hasCompare) {
5558 SelectAArch64Cmp(newCondOpnd, CreateImmOperand(0, ctype, false), true, GetPrimTypeBitSize(ctype));
5559 cc = CC_NE;
5560 }
5561 Operand &newResOpnd = LoadIntoRegister(resOpnd, dtype);
5562 SelectAArch64Select(newResOpnd, newTrueOpnd, newFalseOpnd, GetCondOperand(cc), isIntType,
5563 GetPrimTypeBitSize(dtype));
5564 }
5565
SelectSelect(TernaryNode & expr,Operand & cond,Operand & trueOpnd,Operand & falseOpnd,const BaseNode & parent,bool hasCompare)5566 Operand *AArch64CGFunc::SelectSelect(TernaryNode &expr, Operand &cond, Operand &trueOpnd, Operand &falseOpnd,
5567 const BaseNode &parent, bool hasCompare)
5568 {
5569 PrimType dtype = expr.GetPrimType();
5570 PrimType ctype = expr.Opnd(0)->GetPrimType();
5571
5572 ConditionCode cc = CC_NE;
5573 Opcode opcode = expr.Opnd(0)->GetOpCode();
5574 PrimType cmpType = static_cast<CompareNode *>(expr.Opnd(0))->GetOpndType();
5575 bool isFloat = false;
5576 bool unsignedIntegerComparison = false;
5577 if (!IsPrimitiveVector(cmpType)) {
5578 isFloat = IsPrimitiveFloat(cmpType);
5579 unsignedIntegerComparison = !isFloat && !IsSignedInteger(cmpType);
5580 } else {
5581 isFloat = IsPrimitiveVectorFloat(cmpType);
5582 unsignedIntegerComparison = !isFloat && IsPrimitiveUnSignedVector(cmpType);
5583 }
5584 switch (opcode) {
5585 case OP_eq:
5586 cc = CC_EQ;
5587 break;
5588 case OP_ne:
5589 cc = CC_NE;
5590 break;
5591 case OP_le:
5592 cc = unsignedIntegerComparison ? CC_LS : CC_LE;
5593 break;
5594 case OP_ge:
5595 cc = unsignedIntegerComparison ? CC_HS : CC_GE;
5596 break;
5597 case OP_gt:
5598 cc = unsignedIntegerComparison ? CC_HI : CC_GT;
5599 break;
5600 case OP_lt:
5601 cc = unsignedIntegerComparison ? CC_LO : CC_LT;
5602 break;
5603 default:
5604 hasCompare = true;
5605 break;
5606 }
5607 if (!IsPrimitiveVector(dtype)) {
5608 RegOperand &resOpnd = GetOrCreateResOperand(parent, dtype);
5609 SelectSelect(resOpnd, cond, trueOpnd, falseOpnd, dtype, ctype, hasCompare, cc);
5610 return &resOpnd;
5611 } else {
5612 return SelectVectorSelect(cond, dtype, trueOpnd, falseOpnd);
5613 }
5614 }
5615
5616 /*
5617 * syntax: select <prim-type> (<opnd0>, <opnd1>, <opnd2>)
5618 * <opnd0> must be of integer type.
5619 * <opnd1> and <opnd2> must be of the type given by <prim-type>.
5620 * If <opnd0> is not 0, return <opnd1>. Otherwise, return <opnd2>.
5621 */
SelectAArch64Select(Operand & dest,Operand & o0,Operand & o1,CondOperand & cond,bool isIntType,uint32 dsize)5622 void AArch64CGFunc::SelectAArch64Select(Operand &dest, Operand &o0, Operand &o1, CondOperand &cond, bool isIntType,
5623 uint32 dsize)
5624 {
5625 uint32 mOpCode =
5626 isIntType ? ((dsize == k64BitSize) ? MOP_xcselrrrc : MOP_wcselrrrc)
5627 : ((dsize == k64BitSize) ? MOP_dcselrrrc : ((dsize == k32BitSize) ? MOP_scselrrrc : MOP_hcselrrrc));
5628 Operand &rflag = GetOrCreateRflag();
5629 if (o1.IsImmediate()) {
5630 uint32 movOp = (dsize == k64BitSize ? MOP_xmovri64 : MOP_wmovri32);
5631 RegOperand &movDest =
5632 CreateVirtualRegisterOperand(NewVReg(kRegTyInt, (dsize == k64BitSize) ? k8ByteSize : k4ByteSize));
5633 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(movOp, movDest, o1));
5634 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, dest, o0, movDest, cond, rflag));
5635 return;
5636 }
5637 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOpCode, dest, o0, o1, cond, rflag));
5638 }
5639
SelectRangeGoto(RangeGotoNode & rangeGotoNode,Operand & srcOpnd)5640 void AArch64CGFunc::SelectRangeGoto(RangeGotoNode &rangeGotoNode, Operand &srcOpnd)
5641 {
5642 const SmallCaseVector &switchTable = rangeGotoNode.GetRangeGotoTable();
5643 MIRType *etype = GlobalTables::GetTypeTable().GetTypeFromTyIdx(static_cast<TyIdx>(PTY_a64));
5644 /*
5645 * we store 8-byte displacement ( jump_label - offset_table_address )
5646 * in the table. Refer to AArch64Emit::Emit() in aarch64emit.cpp
5647 */
5648 std::vector<uint64> sizeArray;
5649 sizeArray.emplace_back(switchTable.size());
5650 MIRArrayType *arrayType = memPool->New<MIRArrayType>(etype->GetTypeIndex(), sizeArray);
5651 MIRAggConst *arrayConst = memPool->New<MIRAggConst>(mirModule, *arrayType);
5652 for (const auto &itPair : switchTable) {
5653 LabelIdx labelIdx = itPair.second;
5654 GetCurBB()->PushBackRangeGotoLabel(labelIdx);
5655 MIRConst *mirConst = memPool->New<MIRLblConst>(labelIdx, GetFunction().GetPuidx(), *etype);
5656 arrayConst->AddItem(mirConst, 0);
5657 }
5658
5659 MIRSymbol *lblSt = GetFunction().GetSymTab()->CreateSymbol(kScopeLocal);
5660 lblSt->SetStorageClass(kScFstatic);
5661 lblSt->SetSKind(kStConst);
5662 lblSt->SetTyIdx(arrayType->GetTypeIndex());
5663 lblSt->SetKonst(arrayConst);
5664 std::string lblStr(".LB_");
5665 MIRSymbol *funcSt = GlobalTables::GetGsymTable().GetSymbolFromStidx(GetFunction().GetStIdx().Idx());
5666 CHECK_FATAL(funcSt != nullptr, "funcSt should not be nullptr");
5667 uint32 labelIdxTmp = GetLabelIdx();
5668 lblStr += funcSt->GetName();
5669 lblStr += std::to_string(labelIdxTmp++);
5670 SetLabelIdx(labelIdxTmp);
5671 lblSt->SetNameStrIdx(lblStr);
5672 AddEmitSt(GetCurBB()->GetId(), *lblSt);
5673
5674 PrimType itype = rangeGotoNode.Opnd(0)->GetPrimType();
5675 Operand &opnd0 = LoadIntoRegister(srcOpnd, itype);
5676
5677 regno_t vRegNO = NewVReg(kRegTyInt, 8u);
5678 RegOperand *addOpnd = &CreateVirtualRegisterOperand(vRegNO);
5679
5680 int32 minIdx = switchTable[0].first;
5681 SelectAdd(*addOpnd, opnd0,
5682 CreateImmOperand(-static_cast<int64>(minIdx) - static_cast<int64>(rangeGotoNode.GetTagOffset()),
5683 GetPrimTypeBitSize(itype), true),
5684 itype);
5685
5686 /* contains the index */
5687 if (addOpnd->GetSize() != GetPrimTypeBitSize(PTY_u64)) {
5688 addOpnd = static_cast<RegOperand *>(&SelectCopy(*addOpnd, PTY_u64, PTY_u64));
5689 }
5690
5691 RegOperand &baseOpnd = CreateRegisterOperandOfType(PTY_u64);
5692 StImmOperand &stOpnd = CreateStImmOperand(*lblSt, 0, 0);
5693
5694 /* load the address of the switch table */
5695 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xadrp, baseOpnd, stOpnd));
5696 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xadrpl12, baseOpnd, baseOpnd, stOpnd));
5697
5698 /* load the displacement into a register by accessing memory at base + index*8 */
5699 Operand *disp = CreateMemOperand(MemOperand::kAddrModeBOrX, k64BitSize, baseOpnd, *addOpnd, k8BitShift);
5700 RegOperand &tgt = CreateRegisterOperandOfType(PTY_a64);
5701 SelectAdd(tgt, baseOpnd, *disp, PTY_u64);
5702 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xbr, tgt));
5703 }
5704
SelectLazyLoad(Operand & opnd0,PrimType primType)5705 Operand *AArch64CGFunc::SelectLazyLoad(Operand &opnd0, PrimType primType)
5706 {
5707 DEBUG_ASSERT(opnd0.IsRegister(), "wrong type.");
5708 RegOperand &resOpnd = CreateRegisterOperandOfType(primType);
5709 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_lazy_ldr, resOpnd, opnd0));
5710 return &resOpnd;
5711 }
5712
SelectLazyLoadStatic(MIRSymbol & st,int64 offset,PrimType primType)5713 Operand *AArch64CGFunc::SelectLazyLoadStatic(MIRSymbol &st, int64 offset, PrimType primType)
5714 {
5715 StImmOperand &srcOpnd = CreateStImmOperand(st, offset, 0);
5716 RegOperand &resOpnd = CreateRegisterOperandOfType(primType);
5717 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_lazy_ldr_static, resOpnd, srcOpnd));
5718 return &resOpnd;
5719 }
5720
SelectLoadArrayClassCache(MIRSymbol & st,int64 offset,PrimType primType)5721 Operand *AArch64CGFunc::SelectLoadArrayClassCache(MIRSymbol &st, int64 offset, PrimType primType)
5722 {
5723 StImmOperand &srcOpnd = CreateStImmOperand(st, offset, 0);
5724 RegOperand &resOpnd = CreateRegisterOperandOfType(primType);
5725 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_arrayclass_cache_ldr, resOpnd, srcOpnd));
5726 return &resOpnd;
5727 }
5728
SelectAlloca(UnaryNode & node,Operand & opnd0)5729 Operand *AArch64CGFunc::SelectAlloca(UnaryNode &node, Operand &opnd0)
5730 {
5731 if (!CGOptions::IsArm64ilp32()) {
5732 DEBUG_ASSERT((node.GetPrimType() == PTY_a64), "wrong type");
5733 }
5734 if (GetCG()->IsLmbc()) {
5735 SetHasVLAOrAlloca(true);
5736 }
5737 PrimType stype = node.Opnd(0)->GetPrimType();
5738 Operand *resOpnd = &opnd0;
5739 if (GetPrimTypeBitSize(stype) < GetPrimTypeBitSize(PTY_u64)) {
5740 resOpnd = &CreateRegisterOperandOfType(PTY_u64);
5741 SelectCvtInt2Int(nullptr, resOpnd, &opnd0, stype, PTY_u64);
5742 }
5743
5744 RegOperand &aliOp = CreateRegisterOperandOfType(PTY_u64);
5745
5746 SelectAdd(aliOp, *resOpnd, CreateImmOperand(kAarch64StackPtrAlignment - 1, k64BitSize, true), PTY_u64);
5747 Operand &shifOpnd = CreateImmOperand(__builtin_ctz(kAarch64StackPtrAlignment), k64BitSize, true);
5748 SelectShift(aliOp, aliOp, shifOpnd, kShiftLright, PTY_u64);
5749 SelectShift(aliOp, aliOp, shifOpnd, kShiftLeft, PTY_u64);
5750 Operand &spOpnd = GetOrCreatePhysicalRegisterOperand(RSP, k64BitSize, kRegTyInt);
5751 SelectSub(spOpnd, spOpnd, aliOp, PTY_u64);
5752 int64 allocaOffset = GetMemlayout()->SizeOfArgsToStackPass();
5753 if (GetCG()->IsLmbc()) {
5754 allocaOffset -= kDivide2 * k8ByteSize;
5755 }
5756 if (allocaOffset > 0) {
5757 RegOperand &resallo = CreateRegisterOperandOfType(PTY_u64);
5758 SelectAdd(resallo, spOpnd, CreateImmOperand(allocaOffset, k64BitSize, true), PTY_u64);
5759 return &resallo;
5760 } else {
5761 return &SelectCopy(spOpnd, PTY_u64, PTY_u64);
5762 }
5763 }
5764
SelectMalloc(UnaryNode & node,Operand & opnd0)5765 Operand *AArch64CGFunc::SelectMalloc(UnaryNode &node, Operand &opnd0)
5766 {
5767 PrimType retType = node.GetPrimType();
5768 DEBUG_ASSERT((retType == PTY_a64), "wrong type");
5769
5770 std::vector<Operand *> opndVec;
5771 RegOperand &resOpnd = CreateRegisterOperandOfType(retType);
5772 opndVec.emplace_back(&resOpnd);
5773 opndVec.emplace_back(&opnd0);
5774 /* Use calloc to make sure allocated memory is zero-initialized */
5775 const std::string &funcName = "calloc";
5776 PrimType srcPty = PTY_u64;
5777 if (opnd0.GetSize() <= k32BitSize) {
5778 srcPty = PTY_u32;
5779 }
5780 Operand &opnd1 = CreateImmOperand(1, srcPty, false);
5781 opndVec.emplace_back(&opnd1);
5782 SelectLibCall(funcName, opndVec, srcPty, retType);
5783 return &resOpnd;
5784 }
5785
SelectGCMalloc(GCMallocNode & node)5786 Operand *AArch64CGFunc::SelectGCMalloc(GCMallocNode &node)
5787 {
5788 PrimType retType = node.GetPrimType();
5789 DEBUG_ASSERT((retType == PTY_a64), "wrong type");
5790
5791 /* Get the size and alignment of the type. */
5792 TyIdx tyIdx = node.GetTyIdx();
5793 uint64 size = GetBecommon().GetTypeSize(tyIdx);
5794 uint8 align = RTSupport::GetRTSupportInstance().GetObjectAlignment();
5795
5796 /* Generate the call to MCC_NewObj */
5797 Operand &opndSize = CreateImmOperand(static_cast<int64>(size), k64BitSize, false);
5798 Operand &opndAlign = CreateImmOperand(align, k64BitSize, false);
5799
5800 RegOperand &resOpnd = CreateRegisterOperandOfType(retType);
5801
5802 std::vector<Operand *> opndVec {&resOpnd, &opndSize, &opndAlign};
5803
5804 const std::string &funcName = "MCC_NewObj";
5805 SelectLibCall(funcName, opndVec, PTY_u64, retType);
5806
5807 return &resOpnd;
5808 }
5809
SelectJarrayMalloc(JarrayMallocNode & node,Operand & opnd0)5810 Operand *AArch64CGFunc::SelectJarrayMalloc(JarrayMallocNode &node, Operand &opnd0)
5811 {
5812 PrimType retType = node.GetPrimType();
5813 DEBUG_ASSERT((retType == PTY_a64), "wrong type");
5814
5815 /* Extract jarray type */
5816 TyIdx tyIdx = node.GetTyIdx();
5817 MIRType *type = GlobalTables::GetTypeTable().GetTypeFromTyIdx(tyIdx);
5818 DEBUG_ASSERT(type != nullptr, "nullptr check");
5819 CHECK_FATAL(type->GetKind() == kTypeJArray, "expect MIRJarrayType");
5820 auto jaryType = static_cast<MIRJarrayType *>(type);
5821 uint64 fixedSize = RTSupport::GetRTSupportInstance().GetArrayContentOffset();
5822 uint8 align = RTSupport::GetRTSupportInstance().GetObjectAlignment();
5823
5824 MIRType *elemType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(jaryType->GetElemTyIdx());
5825 PrimType elemPrimType = elemType->GetPrimType();
5826 uint64 elemSize = GetPrimTypeSize(elemPrimType);
5827
5828 /* Generate the cal to MCC_NewObj_flexible */
5829 Operand &opndFixedSize = CreateImmOperand(PTY_u64, static_cast<int64>(fixedSize));
5830 Operand &opndElemSize = CreateImmOperand(PTY_u64, static_cast<int64>(elemSize));
5831
5832 Operand *opndNElems = &opnd0;
5833
5834 Operand *opndNElems64 = &static_cast<Operand &>(CreateRegisterOperandOfType(PTY_u64));
5835 SelectCvtInt2Int(nullptr, opndNElems64, opndNElems, PTY_u32, PTY_u64);
5836
5837 Operand &opndAlign = CreateImmOperand(PTY_u64, align);
5838
5839 RegOperand &resOpnd = CreateRegisterOperandOfType(retType);
5840
5841 std::vector<Operand *> opndVec {&resOpnd, &opndFixedSize, &opndElemSize, opndNElems64, &opndAlign};
5842
5843 const std::string &funcName = "MCC_NewObj_flexible";
5844 SelectLibCall(funcName, opndVec, PTY_u64, retType);
5845
5846 /* Generate the store of the object length field */
5847 MemOperand &opndArrayLengthField =
5848 CreateMemOpnd(resOpnd, static_cast<int64>(RTSupport::GetRTSupportInstance().GetArrayLengthOffset()), k4BitSize);
5849 RegOperand *regOpndNElems = &SelectCopy(*opndNElems, PTY_u32, PTY_u32);
5850 DEBUG_ASSERT(regOpndNElems != nullptr, "null ptr check!");
5851 SelectCopy(opndArrayLengthField, PTY_u32, *regOpndNElems, PTY_u32);
5852
5853 return &resOpnd;
5854 }
5855
IsRegRematCand(const RegOperand & reg) const5856 bool AArch64CGFunc::IsRegRematCand(const RegOperand ®) const
5857 {
5858 MIRPreg *preg = GetPseudoRegFromVirtualRegNO(reg.GetRegisterNumber(), CGOptions::DoCGSSA());
5859 if (preg != nullptr && preg->GetOp() != OP_undef) {
5860 if (preg->GetOp() == OP_constval && cg->GetRematLevel() >= kRematConst) {
5861 return true;
5862 } else if (preg->GetOp() == OP_addrof && cg->GetRematLevel() >= kRematAddr) {
5863 return true;
5864 } else if (preg->GetOp() == OP_iread && cg->GetRematLevel() >= kRematDreadGlobal) {
5865 return true;
5866 } else {
5867 return false;
5868 }
5869 } else {
5870 return false;
5871 }
5872 }
5873
ClearRegRematInfo(const RegOperand & reg) const5874 void AArch64CGFunc::ClearRegRematInfo(const RegOperand ®) const
5875 {
5876 MIRPreg *preg = GetPseudoRegFromVirtualRegNO(reg.GetRegisterNumber(), CGOptions::DoCGSSA());
5877 if (preg != nullptr && preg->GetOp() != OP_undef) {
5878 preg->SetOp(OP_undef);
5879 }
5880 }
5881
IsRegSameRematInfo(const RegOperand & regDest,const RegOperand & regSrc) const5882 bool AArch64CGFunc::IsRegSameRematInfo(const RegOperand ®Dest, const RegOperand ®Src) const
5883 {
5884 MIRPreg *pregDest = GetPseudoRegFromVirtualRegNO(regDest.GetRegisterNumber(), CGOptions::DoCGSSA());
5885 MIRPreg *pregSrc = GetPseudoRegFromVirtualRegNO(regSrc.GetRegisterNumber(), CGOptions::DoCGSSA());
5886 if (pregDest != nullptr && pregDest == pregSrc) {
5887 if (pregDest->GetOp() == OP_constval && cg->GetRematLevel() >= kRematConst) {
5888 return true;
5889 } else if (pregDest->GetOp() == OP_addrof && cg->GetRematLevel() >= kRematAddr) {
5890 return true;
5891 } else if (pregDest->GetOp() == OP_iread && cg->GetRematLevel() >= kRematDreadGlobal) {
5892 return true;
5893 } else {
5894 return false;
5895 }
5896 } else {
5897 return false;
5898 }
5899 }
5900
ReplaceOpndInInsn(RegOperand & regDest,RegOperand & regSrc,Insn & insn,regno_t destNO)5901 void AArch64CGFunc::ReplaceOpndInInsn(RegOperand ®Dest, RegOperand ®Src, Insn &insn, regno_t destNO)
5902 {
5903 auto opndNum = static_cast<int32>(insn.GetOperandSize());
5904 for (int i = opndNum - 1; i >= 0; --i) {
5905 Operand &opnd = insn.GetOperand(static_cast<uint32>(i));
5906 if (opnd.IsList()) {
5907 std::list<RegOperand *> tempRegStore;
5908 auto &opndList = static_cast<ListOperand &>(opnd).GetOperands();
5909 bool needReplace = false;
5910 for (auto it = opndList.cbegin(), end = opndList.cend(); it != end; ++it) {
5911 auto *regOpnd = *it;
5912 if (regOpnd->GetRegisterNumber() == destNO) {
5913 needReplace = true;
5914 tempRegStore.push_back(®Src);
5915 } else {
5916 tempRegStore.push_back(regOpnd);
5917 }
5918 }
5919 if (needReplace) {
5920 opndList.clear();
5921 for (auto &newOpnd : std::as_const(tempRegStore)) {
5922 static_cast<ListOperand &>(opnd).PushOpnd(*newOpnd);
5923 }
5924 }
5925 } else if (opnd.IsMemoryAccessOperand()) {
5926 auto &memOpnd = static_cast<MemOperand &>(opnd);
5927 RegOperand *baseRegOpnd = memOpnd.GetBaseRegister();
5928 RegOperand *indexRegOpnd = memOpnd.GetIndexRegister();
5929 MemOperand *newMem = static_cast<MemOperand *>(memOpnd.Clone(*GetMemoryPool()));
5930 if ((baseRegOpnd != nullptr && baseRegOpnd->GetRegisterNumber() == destNO) ||
5931 (indexRegOpnd != nullptr && indexRegOpnd->GetRegisterNumber() == destNO)) {
5932 if (baseRegOpnd != nullptr && baseRegOpnd->GetRegisterNumber() == destNO) {
5933 newMem->SetBaseRegister(regSrc);
5934 }
5935 if (indexRegOpnd != nullptr && indexRegOpnd->GetRegisterNumber() == destNO) {
5936 auto *newRegSrc = static_cast<RegOperand *>(regSrc.Clone(*GetMemoryPool()));
5937 newRegSrc->SetSize(indexRegOpnd->GetSize()); // retain the original size
5938 newMem->SetIndexRegister(*newRegSrc);
5939 }
5940 insn.SetMemOpnd(newMem);
5941 }
5942 } else if (opnd.IsRegister()) {
5943 auto ®Opnd = static_cast<RegOperand &>(opnd);
5944 if (regOpnd.GetRegisterNumber() == destNO) {
5945 DEBUG_ASSERT(regOpnd.GetRegisterNumber() != kRFLAG, "both condi and reg");
5946 if (regOpnd.GetBaseRefOpnd() != nullptr) {
5947 regSrc.SetBaseRefOpnd(*regOpnd.GetBaseRefOpnd());
5948 ReplaceRegReference(regOpnd.GetRegisterNumber(), regSrc.GetRegisterNumber());
5949 }
5950 CHECK_FATAL(regOpnd.GetSize() == regSrc.GetSize(), "NIY");
5951 insn.SetOperand(static_cast<uint32>(i), regSrc);
5952 }
5953 if (!IsStackMapComputed() && regOpnd.GetBaseRefOpnd() != nullptr &&
5954 regOpnd.GetBaseRefOpnd()->GetRegisterNumber() == destNO) {
5955 regOpnd.SetBaseRefOpnd(regSrc);
5956 ReplaceRegReference(regOpnd.GetBaseRefOpnd()->GetRegisterNumber(), regSrc.GetRegisterNumber());
5957 }
5958 }
5959 }
5960 if (!IsStackMapComputed() && insn.GetStackMap() != nullptr) {
5961 for (auto [deoptVreg, opnd] : insn.GetStackMap()->GetDeoptInfo().GetDeoptBundleInfo()) {
5962 if (!opnd->IsRegister()) {
5963 continue;
5964 }
5965 auto ®Opnd = static_cast<RegOperand &>(*opnd);
5966 if (regOpnd.GetRegisterNumber() == destNO) {
5967 insn.GetStackMap()->GetDeoptInfo().ReplaceDeoptBundleInfo(deoptVreg, regSrc);
5968 ReplaceRegReference(regOpnd.GetRegisterNumber(), regSrc.GetRegisterNumber());
5969 }
5970 }
5971 }
5972 }
5973
CleanupDeadMov(bool dumpInfo)5974 void AArch64CGFunc::CleanupDeadMov(bool dumpInfo)
5975 {
5976 /* clean dead mov. */
5977 FOR_ALL_BB(bb, this)
5978 {
5979 FOR_BB_INSNS_SAFE(insn, bb, ninsn)
5980 {
5981 if (!insn->IsMachineInstruction()) {
5982 continue;
5983 }
5984 if (insn->GetMachineOpcode() == MOP_xmovrr || insn->GetMachineOpcode() == MOP_wmovrr ||
5985 insn->GetMachineOpcode() == MOP_xvmovs || insn->GetMachineOpcode() == MOP_xvmovd) {
5986 RegOperand ®Dest = static_cast<RegOperand &>(insn->GetOperand(kInsnFirstOpnd));
5987 RegOperand ®Src = static_cast<RegOperand &>(insn->GetOperand(kInsnSecondOpnd));
5988 if (!regSrc.IsVirtualRegister() || !regDest.IsVirtualRegister()) {
5989 continue;
5990 }
5991
5992 if (regSrc.GetRegisterNumber() == regDest.GetRegisterNumber()) {
5993 bb->RemoveInsn(*insn);
5994 } else if (insn->IsPhiMovInsn() && dumpInfo) {
5995 LogInfo::MapleLogger() << "fail to remove mov: " << regDest.GetRegisterNumber() << " <- "
5996 << regSrc.GetRegisterNumber() << std::endl;
5997 }
5998 }
5999 }
6000 }
6001 }
6002
GetRealCallerSaveRegs(const Insn & insn,std::set<regno_t> & realSaveRegs)6003 void AArch64CGFunc::GetRealCallerSaveRegs(const Insn &insn, std::set<regno_t> &realSaveRegs)
6004 {
6005 auto *targetOpnd = insn.GetCallTargetOperand();
6006 CHECK_FATAL(targetOpnd != nullptr, "target is null in AArch64Insn::IsCallToFunctionThatNeverReturns");
6007 if (CGOptions::DoIPARA() && targetOpnd->IsFuncNameOpnd()) {
6008 FuncNameOperand *target = static_cast<FuncNameOperand *>(targetOpnd);
6009 const MIRSymbol *funcSt = target->GetFunctionSymbol();
6010 DEBUG_ASSERT(funcSt->GetSKind() == kStFunc, "funcst must be a function name symbol");
6011 MIRFunction *func = funcSt->GetFunction();
6012 if (func != nullptr && func->IsReferedRegsValid()) {
6013 for (auto preg : func->GetReferedRegs()) {
6014 if (AArch64Abi::IsCallerSaveReg(static_cast<AArch64reg>(preg))) {
6015 realSaveRegs.insert(preg);
6016 }
6017 }
6018 return;
6019 }
6020 }
6021 for (uint32 i = R0; i <= kMaxRegNum; ++i) {
6022 if (AArch64Abi::IsCallerSaveReg(static_cast<AArch64reg>(i))) {
6023 realSaveRegs.insert(i);
6024 }
6025 }
6026 }
6027
GetZeroOpnd(uint32 bitLen)6028 RegOperand &AArch64CGFunc::GetZeroOpnd(uint32 bitLen)
6029 {
6030 /*
6031 * It is possible to have a bitLen < 32, eg stb.
6032 * Set it to 32 if it is less than 32.
6033 */
6034 if (bitLen < k32BitSize) {
6035 bitLen = k32BitSize;
6036 }
6037 DEBUG_ASSERT((bitLen == k32BitSize || bitLen == k64BitSize), "illegal bit length = %d", bitLen);
6038 return (bitLen == k32BitSize) ? GetOrCreatePhysicalRegisterOperand(RZR, k32BitSize, kRegTyInt)
6039 : GetOrCreatePhysicalRegisterOperand(RZR, k64BitSize, kRegTyInt);
6040 }
6041
IsFrameReg(const RegOperand & opnd) const6042 bool AArch64CGFunc::IsFrameReg(const RegOperand &opnd) const
6043 {
6044 if (opnd.GetRegisterNumber() == RFP) {
6045 return true;
6046 } else {
6047 return false;
6048 }
6049 }
6050
IsSaveReg(const RegOperand & reg,MIRType & mirType,BECommon & cgBeCommon)6051 bool AArch64CGFunc::IsSaveReg(const RegOperand ®, MIRType &mirType, BECommon &cgBeCommon)
6052 {
6053 CCImpl &retLocator = *GetOrCreateLocator(GetCurCallConvKind());
6054 CCLocInfo retMechanism;
6055 retLocator.LocateRetVal(mirType, retMechanism);
6056 if (retMechanism.GetRegCount() > 0) {
6057 return reg.GetRegisterNumber() == retMechanism.GetReg0() || reg.GetRegisterNumber() == retMechanism.GetReg1() ||
6058 reg.GetRegisterNumber() == retMechanism.GetReg2() || reg.GetRegisterNumber() == retMechanism.GetReg3();
6059 }
6060 return false;
6061 }
6062
IsSPOrFP(const RegOperand & opnd) const6063 bool AArch64CGFunc::IsSPOrFP(const RegOperand &opnd) const
6064 {
6065 const RegOperand ®Opnd = static_cast<const RegOperand &>(opnd);
6066 regno_t regNO = opnd.GetRegisterNumber();
6067 return (regOpnd.IsPhysicalRegister() &&
6068 (regNO == RSP || regNO == RFP || (regNO == R29 && CGOptions::UseFramePointer())));
6069 }
6070
IsReturnReg(const RegOperand & opnd) const6071 bool AArch64CGFunc::IsReturnReg(const RegOperand &opnd) const
6072 {
6073 regno_t regNO = opnd.GetRegisterNumber();
6074 return (regNO == R0) || (regNO == V0);
6075 }
6076
6077 /*
6078 * This function returns true to indicate that the clean up code needs to be generated,
6079 * otherwise it does not need. In GCOnly mode, it always returns false.
6080 */
NeedCleanup()6081 bool AArch64CGFunc::NeedCleanup()
6082 {
6083 if (CGOptions::IsGCOnly()) {
6084 return false;
6085 }
6086 AArch64MemLayout *layout = static_cast<AArch64MemLayout *>(GetMemlayout());
6087 if (layout->GetSizeOfRefLocals() > 0) {
6088 return true;
6089 }
6090 for (uint32 i = 0; i < GetFunction().GetFormalCount(); i++) {
6091 TypeAttrs ta = GetFunction().GetNthParamAttr(i);
6092 if (ta.GetAttr(ATTR_localrefvar)) {
6093 return true;
6094 }
6095 }
6096
6097 return false;
6098 }
6099
6100 /*
6101 * bb must be the cleanup bb.
6102 * this function must be invoked before register allocation.
6103 * extended epilogue is specific for fast exception handling and is made up of
6104 * clean up code and epilogue.
6105 * clean up code is generated here while epilogue is generated in GeneratePrologEpilog()
6106 */
GenerateCleanupCodeForExtEpilog(BB & bb)6107 void AArch64CGFunc::GenerateCleanupCodeForExtEpilog(BB &bb)
6108 {
6109 DEBUG_ASSERT(GetLastBB()->GetPrev()->GetFirstStmt() == GetCleanupLabel(), "must be");
6110
6111 if (NeedCleanup()) {
6112 /* this is necessary for code insertion. */
6113 SetCurBB(bb);
6114
6115 RegOperand ®Opnd0 =
6116 GetOrCreatePhysicalRegisterOperand(R0, GetPointerSize() * kBitsPerByte, GetRegTyFromPrimTy(PTY_a64));
6117 RegOperand ®Opnd1 =
6118 GetOrCreatePhysicalRegisterOperand(R1, GetPointerSize() * kBitsPerByte, GetRegTyFromPrimTy(PTY_a64));
6119 /* allocate 16 bytes to store reg0 and reg1 (each reg has 8 bytes) */
6120 MemOperand &frameAlloc = CreateCallFrameOperand(-16, GetPointerSize() * kBitsPerByte);
6121 Insn &allocInsn = GetInsnBuilder()->BuildInsn(MOP_xstp, regOpnd0, regOpnd1, frameAlloc);
6122 allocInsn.SetDoNotRemove(true);
6123 AppendInstructionTo(allocInsn, *this);
6124
6125 /* invoke MCC_CleanupLocalStackRef(). */
6126 HandleRCCall(false);
6127 /* deallocate 16 bytes which used to store reg0 and reg1 */
6128 MemOperand &frameDealloc = CreateCallFrameOperand(16, GetPointerSize() * kBitsPerByte);
6129 GenRetCleanup(cleanEANode, true);
6130 Insn &deallocInsn = GetInsnBuilder()->BuildInsn(MOP_xldp, regOpnd0, regOpnd1, frameDealloc);
6131 deallocInsn.SetDoNotRemove(true);
6132 AppendInstructionTo(deallocInsn, *this);
6133 /* Update cleanupbb since bb may have been splitted */
6134 SetCleanupBB(*GetCurBB());
6135 }
6136 }
6137
6138 /*
6139 * bb must be the cleanup bb.
6140 * this function must be invoked before register allocation.
6141 */
GenerateCleanupCode(BB & bb)6142 void AArch64CGFunc::GenerateCleanupCode(BB &bb)
6143 {
6144 DEBUG_ASSERT(GetLastBB()->GetPrev()->GetFirstStmt() == GetCleanupLabel(), "must be");
6145 if (!NeedCleanup()) {
6146 return;
6147 }
6148
6149 /* this is necessary for code insertion. */
6150 SetCurBB(bb);
6151
6152 /* R0 is lived-in for clean-up code, save R0 before invocation */
6153 RegOperand &livein = GetOrCreatePhysicalRegisterOperand(R0, k64BitSize, GetRegTyFromPrimTy(PTY_a64));
6154
6155 if (!GetCG()->GenLocalRC()) {
6156 /* by pass local RC operations. */
6157 } else if (Globals::GetInstance()->GetOptimLevel() > CGOptions::kLevel0) {
6158 regno_t vreg = NewVReg(GetRegTyFromPrimTy(PTY_a64), GetPrimTypeSize(PTY_a64));
6159 RegOperand &backupRegOp = CreateVirtualRegisterOperand(vreg);
6160 backupRegOp.SetRegNotBBLocal();
6161 SelectCopy(backupRegOp, PTY_a64, livein, PTY_a64);
6162
6163 /* invoke MCC_CleanupLocalStackRef(). */
6164 HandleRCCall(false);
6165 SelectCopy(livein, PTY_a64, backupRegOp, PTY_a64);
6166 } else {
6167 /*
6168 * Register Allocation for O0 can not handle this case, so use a callee saved register directly.
6169 * If yieldpoint is enabled, we use R20 instead R19.
6170 */
6171 AArch64reg backupRegNO = GetCG()->GenYieldPoint() ? R20 : R19;
6172 RegOperand &backupRegOp =
6173 GetOrCreatePhysicalRegisterOperand(backupRegNO, k64BitSize, GetRegTyFromPrimTy(PTY_a64));
6174 SelectCopy(backupRegOp, PTY_a64, livein, PTY_a64);
6175 /* invoke MCC_CleanupLocalStackRef(). */
6176 HandleRCCall(false);
6177 SelectCopy(livein, PTY_a64, backupRegOp, PTY_a64);
6178 }
6179
6180 /* invoke _Unwind_Resume */
6181 std::string funcName("_Unwind_Resume");
6182 MIRSymbol *sym = GlobalTables::GetGsymTable().CreateSymbol(kScopeGlobal);
6183 sym->SetNameStrIdx(funcName);
6184 sym->SetStorageClass(kScText);
6185 sym->SetSKind(kStFunc);
6186 ListOperand *srcOpnds = CreateListOpnd(*GetFuncScopeAllocator());
6187 srcOpnds->PushOpnd(livein);
6188 AppendCall(*sym, *srcOpnds);
6189 /*
6190 * this instruction is unreachable, but we need it as the return address of previous
6191 * "bl _Unwind_Resume" for stack unwinding.
6192 */
6193 Insn &nop = GetInsnBuilder()->BuildInsn(MOP_xblr, livein, *srcOpnds);
6194 GetCurBB()->AppendInsn(nop);
6195 GetCurBB()->SetHasCall();
6196
6197 /* Update cleanupbb since bb may have been splitted */
6198 SetCleanupBB(*GetCurBB());
6199 }
6200
FloatParamRegRequired(MIRStructType * structType,uint32 & fpSize)6201 uint32 AArch64CGFunc::FloatParamRegRequired(MIRStructType *structType, uint32 &fpSize)
6202 {
6203 AArch64CallConvImpl parmlocator(GetBecommon());
6204 return parmlocator.FloatParamRegRequired(*structType, fpSize);
6205 }
6206
6207 /*
6208 * Map param registers to formals. For small structs passed in param registers,
6209 * create a move to vreg since lmbc IR does not create a regassign for them.
6210 */
AssignLmbcFormalParams()6211 void AArch64CGFunc::AssignLmbcFormalParams()
6212 {
6213 PrimType primType;
6214 uint32 offset;
6215 regno_t intReg = R0;
6216 regno_t fpReg = V0;
6217 for (auto param : GetLmbcParamVec()) {
6218 primType = param->GetPrimType();
6219 offset = param->GetOffset();
6220 if (param->IsReturn()) {
6221 param->SetRegNO(R8);
6222 } else if (IsPrimitiveInteger(primType)) {
6223 if (intReg > R7) {
6224 param->SetRegNO(0);
6225 } else {
6226 param->SetRegNO(intReg);
6227 if (!param->HasRegassign()) {
6228 uint32 bytelen = GetPrimTypeSize(primType);
6229 uint32 bitlen = bytelen * kBitsPerByte;
6230 MemOperand *mOpnd = GenLmbcFpMemOperand(static_cast<int32>(offset), bytelen);
6231 RegOperand &src = GetOrCreatePhysicalRegisterOperand(AArch64reg(intReg), bitlen, kRegTyInt);
6232 MOperator mOp = PickStInsn(bitlen, primType);
6233 Insn &store = GetInsnBuilder()->BuildInsn(mOp, src, *mOpnd);
6234 GetCurBB()->AppendInsn(store);
6235 }
6236 intReg++;
6237 }
6238 } else if (IsPrimitiveFloat(primType)) {
6239 if (fpReg > V7) {
6240 param->SetRegNO(0);
6241 } else {
6242 param->SetRegNO(fpReg);
6243 if (!param->HasRegassign()) {
6244 uint32 bytelen = GetPrimTypeSize(primType);
6245 uint32 bitlen = bytelen * kBitsPerByte;
6246 MemOperand *mOpnd = GenLmbcFpMemOperand(static_cast<int32>(offset), bytelen);
6247 RegOperand &src = GetOrCreatePhysicalRegisterOperand(AArch64reg(fpReg), bitlen, kRegTyFloat);
6248 MOperator mOp = PickStInsn(bitlen, primType);
6249 Insn &store = GetInsnBuilder()->BuildInsn(mOp, src, *mOpnd);
6250 GetCurBB()->AppendInsn(store);
6251 }
6252 fpReg++;
6253 }
6254 } else if (primType == PTY_agg) {
6255 if (param->IsPureFloat()) {
6256 uint32 numFpRegs = param->GetNumRegs();
6257 if ((fpReg + numFpRegs - kOneRegister) > V7) {
6258 param->SetRegNO(0);
6259 } else {
6260 param->SetRegNO(fpReg);
6261 param->SetNumRegs(numFpRegs);
6262 fpReg += numFpRegs;
6263 }
6264 } else if (param->GetSize() > k16ByteSize) {
6265 if (intReg > R7) {
6266 param->SetRegNO(0);
6267 } else {
6268 param->SetRegNO(intReg);
6269 param->SetIsOnStack();
6270 param->SetOnStackOffset(((intReg - R0 + fpReg) - V0) * k8ByteSize);
6271 uint32 bytelen = GetPrimTypeSize(PTY_a64);
6272 uint32 bitlen = bytelen * kBitsPerByte;
6273 MemOperand *mOpnd = GenLmbcFpMemOperand(static_cast<int32>(param->GetOnStackOffset()), bytelen);
6274 RegOperand &src = GetOrCreatePhysicalRegisterOperand(AArch64reg(intReg), bitlen, kRegTyInt);
6275 MOperator mOp = PickStInsn(bitlen, PTY_a64);
6276 Insn &store = GetInsnBuilder()->BuildInsn(mOp, src, *mOpnd);
6277 GetCurBB()->AppendInsn(store);
6278 intReg++;
6279 }
6280 } else if (param->GetSize() <= k8ByteSize) {
6281 if (intReg > R7) {
6282 param->SetRegNO(0);
6283 } else {
6284 param->SetRegNO(intReg);
6285 param->SetNumRegs(kOneRegister);
6286 intReg++;
6287 }
6288 } else {
6289 /* size > 8 && size <= 16 */
6290 if ((intReg + kOneRegister) > R7) {
6291 param->SetRegNO(0);
6292 } else {
6293 param->SetRegNO(intReg);
6294 param->SetNumRegs(kTwoRegister);
6295 intReg += kTwoRegister;
6296 }
6297 }
6298 if (param->GetRegNO() != 0) {
6299 for (uint32 i = 0; i < param->GetNumRegs(); ++i) {
6300 PrimType pType = PTY_i64;
6301 RegType rType = kRegTyInt;
6302 uint32 rSize = k8ByteSize;
6303 if (param->IsPureFloat()) {
6304 rType = kRegTyFloat;
6305 if (param->GetFpSize() <= k4ByteSize) {
6306 pType = PTY_f32;
6307 rSize = k4ByteSize;
6308 } else {
6309 pType = PTY_f64;
6310 }
6311 }
6312 regno_t vreg = NewVReg(rType, rSize);
6313 RegOperand &dest = GetOrCreateVirtualRegisterOperand(vreg);
6314 RegOperand &src = GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(param->GetRegNO() + i),
6315 rSize * kBitsPerByte, rType);
6316 SelectCopy(dest, pType, src, pType);
6317 if (param->GetVregNO() == 0) {
6318 param->SetVregNO(vreg);
6319 }
6320 Operand *memOpd = &CreateMemOpnd(RFP, offset + (i * rSize), rSize);
6321 GetCurBB()->AppendInsn(
6322 GetInsnBuilder()->BuildInsn(PickStInsn(rSize * kBitsPerByte, pType), dest, *memOpd));
6323 }
6324 }
6325 } else {
6326 CHECK_FATAL(false, "lmbc formal primtype not handled");
6327 }
6328 }
6329 }
6330
LmbcGenSaveSpForAlloca()6331 void AArch64CGFunc::LmbcGenSaveSpForAlloca()
6332 {
6333 if (GetMirModule().GetFlavor() != MIRFlavor::kFlavorLmbc || !HasVLAOrAlloca()) {
6334 return;
6335 }
6336 Operand &spOpnd = GetOrCreatePhysicalRegisterOperand(RSP, k64BitSize, kRegTyInt);
6337 regno_t regno = NewVReg(kRegTyInt, GetPointerSize());
6338 RegOperand &spSaveOpnd = CreateVirtualRegisterOperand(regno);
6339 SetSpSaveReg(regno);
6340 Insn &save = GetInsnBuilder()->BuildInsn(MOP_xmovrr, spSaveOpnd, spOpnd);
6341 GetFirstBB()->AppendInsn(save);
6342 for (auto *retBB : GetExitBBsVec()) {
6343 Insn &restore = GetInsnBuilder()->BuildInsn(MOP_xmovrr, spOpnd, spSaveOpnd);
6344 retBB->AppendInsn(restore);
6345 restore.SetFrameDef(true);
6346 }
6347 }
6348
6349 /* if offset < 0, allocation; otherwise, deallocation */
CreateCallFrameOperand(int32 offset,uint32 size)6350 MemOperand &AArch64CGFunc::CreateCallFrameOperand(int32 offset, uint32 size)
6351 {
6352 MemOperand *memOpnd = CreateStackMemOpnd(RSP, offset, size);
6353 memOpnd->SetIndexOpt((offset < 0) ? MemOperand::kPreIndex : MemOperand::kPostIndex);
6354 return *memOpnd;
6355 }
6356
GetLogicalShiftLeftOperand(uint32 shiftAmount,bool is64bits) const6357 BitShiftOperand *AArch64CGFunc::GetLogicalShiftLeftOperand(uint32 shiftAmount, bool is64bits) const
6358 {
6359 /* num(0, 16, 32, 48) >> 4 is num1(0, 1, 2, 3), num1 & (~3) == 0 */
6360 DEBUG_ASSERT((!shiftAmount || ((shiftAmount >> 4) & ~static_cast<uint32>(3)) == 0),
6361 "shift amount should be one of 0, 16, 32, 48");
6362 /* movkLslOperands[4]~movkLslOperands[7] is for 64 bits */
6363 return &movkLslOperands[(shiftAmount >> 4) + (is64bits ? 4 : 0)];
6364 }
6365
6366 AArch64CGFunc::MovkLslOperandArray AArch64CGFunc::movkLslOperands = {
6367 BitShiftOperand(BitShiftOperand::kLSL, 0, 4),
6368 BitShiftOperand(BitShiftOperand::kLSL, 16, 4),
6369 BitShiftOperand(BitShiftOperand::kLSL, static_cast<uint32>(-1), 0), /* invalid entry */
6370 BitShiftOperand(BitShiftOperand::kLSL, static_cast<uint32>(-1), 0), /* invalid entry */
6371 BitShiftOperand(BitShiftOperand::kLSL, 0, 6),
6372 BitShiftOperand(BitShiftOperand::kLSL, 16, 6),
6373 BitShiftOperand(BitShiftOperand::kLSL, 32, 6),
6374 BitShiftOperand(BitShiftOperand::kLSL, 48, 6),
6375 };
6376
CreateStkTopOpnd(uint32 offset,uint32 size)6377 MemOperand &AArch64CGFunc::CreateStkTopOpnd(uint32 offset, uint32 size)
6378 {
6379 AArch64reg reg;
6380 if (GetMirModule().GetFlavor() == MIRFlavor::kFlavorLmbc) {
6381 reg = RSP;
6382 } else {
6383 reg = RFP;
6384 }
6385 MemOperand *memOp = CreateStackMemOpnd(reg, static_cast<int32>(offset), size);
6386 return *memOp;
6387 }
6388
CreateStackMemOpnd(regno_t preg,int32 offset,uint32 size)6389 MemOperand *AArch64CGFunc::CreateStackMemOpnd(regno_t preg, int32 offset, uint32 size)
6390 {
6391 auto *memOp =
6392 memPool->New<MemOperand>(memPool->New<RegOperand>(preg, k64BitSize, kRegTyInt),
6393 &CreateOfstOpnd(static_cast<uint64>(static_cast<int64>(offset)), k32BitSize), size);
6394 if (preg == RFP || preg == RSP) {
6395 memOp->SetStackMem(true);
6396 }
6397 return memOp;
6398 }
6399
6400 /* Mem mod BOI || PreIndex || PostIndex */
CreateMemOperand(uint32 size,RegOperand & base,ImmOperand & ofstOp,bool isVolatile,MemOperand::AArch64AddressingMode mode) const6401 MemOperand *AArch64CGFunc::CreateMemOperand(uint32 size, RegOperand &base, ImmOperand &ofstOp, bool isVolatile,
6402 MemOperand::AArch64AddressingMode mode) const
6403 {
6404 auto *memOp = memPool->New<MemOperand>(size, base, ofstOp, mode);
6405 memOp->SetVolatile(isVolatile);
6406 if (base.GetRegisterNumber() == RFP || base.GetRegisterNumber() == RSP) {
6407 memOp->SetStackMem(true);
6408 }
6409 return memOp;
6410 }
6411
CreateMemOperand(MemOperand::AArch64AddressingMode mode,uint32 size,RegOperand & base,RegOperand * index,ImmOperand * offset,const MIRSymbol * symbol) const6412 MemOperand *AArch64CGFunc::CreateMemOperand(MemOperand::AArch64AddressingMode mode, uint32 size, RegOperand &base,
6413 RegOperand *index, ImmOperand *offset, const MIRSymbol *symbol) const
6414 {
6415 auto *memOp = memPool->New<MemOperand>(mode, size, base, index, offset, symbol);
6416 if (base.GetRegisterNumber() == RFP || base.GetRegisterNumber() == RSP) {
6417 memOp->SetStackMem(true);
6418 }
6419 return memOp;
6420 }
6421
CreateMemOperand(MemOperand::AArch64AddressingMode mode,uint32 size,RegOperand & base,RegOperand & index,ImmOperand * offset,const MIRSymbol & symbol,bool noExtend)6422 MemOperand *AArch64CGFunc::CreateMemOperand(MemOperand::AArch64AddressingMode mode, uint32 size, RegOperand &base,
6423 RegOperand &index, ImmOperand *offset, const MIRSymbol &symbol,
6424 bool noExtend)
6425 {
6426 auto *memOp = memPool->New<MemOperand>(mode, size, base, index, offset, symbol, noExtend);
6427 if (base.GetRegisterNumber() == RFP || base.GetRegisterNumber() == RSP) {
6428 memOp->SetStackMem(true);
6429 }
6430 return memOp;
6431 }
6432
CreateMemOperand(MemOperand::AArch64AddressingMode mode,uint32 dSize,RegOperand & base,RegOperand & indexOpnd,uint32 shift,bool isSigned) const6433 MemOperand *AArch64CGFunc::CreateMemOperand(MemOperand::AArch64AddressingMode mode, uint32 dSize, RegOperand &base,
6434 RegOperand &indexOpnd, uint32 shift, bool isSigned) const
6435 {
6436 auto *memOp = memPool->New<MemOperand>(mode, dSize, base, indexOpnd, shift, isSigned);
6437 if (base.GetRegisterNumber() == RFP || base.GetRegisterNumber() == RSP) {
6438 memOp->SetStackMem(true);
6439 }
6440 return memOp;
6441 }
6442
CreateMemOperand(MemOperand::AArch64AddressingMode mode,uint32 dSize,const MIRSymbol & sym)6443 MemOperand *AArch64CGFunc::CreateMemOperand(MemOperand::AArch64AddressingMode mode, uint32 dSize, const MIRSymbol &sym)
6444 {
6445 auto *memOp = memPool->New<MemOperand>(mode, dSize, sym);
6446 return memOp;
6447 }
6448
GenSaveMethodInfoCode(BB & bb)6449 void AArch64CGFunc::GenSaveMethodInfoCode(BB &bb)
6450 {
6451 if (GetCG()->UseFastUnwind()) {
6452 BB *formerCurBB = GetCurBB();
6453 GetDummyBB()->ClearInsns();
6454 SetCurBB(*GetDummyBB());
6455 if ((GetFunction().GetAttr(FUNCATTR_native) || GetFunction().GetAttr(FUNCATTR_fast_native)) &&
6456 !GetFunction().GetAttr(FUNCATTR_critical_native) && !GetFunction().GetAttr(FUNCATTR_bridge)) {
6457 RegOperand &fpReg = GetOrCreatePhysicalRegisterOperand(RFP, GetPointerSize() * kBitsPerByte, kRegTyInt);
6458
6459 ListOperand *srcOpnds = CreateListOpnd(*GetFuncScopeAllocator());
6460 RegOperand &parmRegOpnd1 = GetOrCreatePhysicalRegisterOperand(R0, k64BitSize, kRegTyInt);
6461 srcOpnds->PushOpnd(parmRegOpnd1);
6462 Operand &immOpnd = CreateImmOperand(0, k64BitSize, false);
6463 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xadri64, parmRegOpnd1, immOpnd));
6464 RegOperand &parmRegOpnd2 = GetOrCreatePhysicalRegisterOperand(R1, k64BitSize, kRegTyInt);
6465 srcOpnds->PushOpnd(parmRegOpnd2);
6466 SelectCopy(parmRegOpnd2, PTY_a64, fpReg, PTY_a64);
6467
6468 MIRSymbol *sym = GlobalTables::GetGsymTable().CreateSymbol(kScopeGlobal);
6469 std::string funcName("MCC_SetRiskyUnwindContext");
6470 sym->SetNameStrIdx(funcName);
6471
6472 sym->SetStorageClass(kScText);
6473 sym->SetSKind(kStFunc);
6474 AppendCall(*sym, *srcOpnds);
6475 bb.SetHasCall();
6476 }
6477
6478 bb.InsertAtBeginning(*GetDummyBB());
6479 SetCurBB(*formerCurBB);
6480 }
6481 }
6482
HasStackLoadStore()6483 bool AArch64CGFunc::HasStackLoadStore()
6484 {
6485 FOR_ALL_BB(bb, this)
6486 {
6487 FOR_BB_INSNS(insn, bb)
6488 {
6489 uint32 opndNum = insn->GetOperandSize();
6490 for (uint32 i = 0; i < opndNum; ++i) {
6491 Operand &opnd = insn->GetOperand(i);
6492 if (opnd.IsMemoryAccessOperand()) {
6493 auto &memOpnd = static_cast<MemOperand &>(opnd);
6494 Operand *base = memOpnd.GetBaseRegister();
6495
6496 if ((base != nullptr) && base->IsRegister()) {
6497 RegOperand *regOpnd = static_cast<RegOperand *>(base);
6498 RegType regType = regOpnd->GetRegisterType();
6499 uint32 regNO = regOpnd->GetRegisterNumber();
6500 if (((regType != kRegTyCc) && ((regNO == RFP) || (regNO == RSP))) || (regType == kRegTyVary)) {
6501 return true;
6502 }
6503 }
6504 }
6505 }
6506 }
6507 }
6508 return false;
6509 }
6510
GenerateYieldpoint(BB & bb)6511 void AArch64CGFunc::GenerateYieldpoint(BB &bb)
6512 {
6513 /* ldr wzr, [RYP] # RYP hold address of the polling page. */
6514 auto &wzr = GetZeroOpnd(k32BitSize);
6515 auto &pollingPage = CreateMemOpnd(RYP, 0, k32BitSize);
6516 auto &yieldPoint = GetInsnBuilder()->BuildInsn(MOP_wldr, wzr, pollingPage);
6517 if (GetCG()->GenerateVerboseCG()) {
6518 yieldPoint.SetComment("yieldpoint");
6519 }
6520 bb.AppendInsn(yieldPoint);
6521 }
6522
GetTargetRetOperand(PrimType primType,int32 sReg)6523 Operand &AArch64CGFunc::GetTargetRetOperand(PrimType primType, int32 sReg)
6524 {
6525 DEBUG_ASSERT(!IsInt128Ty(primType), "NIY");
6526 if (IsSpecialPseudoRegister(-sReg)) {
6527 return GetOrCreateSpecialRegisterOperand(sReg, primType);
6528 }
6529 bool useFpReg = !IsPrimitiveInteger(primType) || IsPrimitiveVectorFloat(primType);
6530 uint32 bitSize = GetPrimTypeBitSize(primType);
6531 bitSize = bitSize <= k32BitSize ? k32BitSize : bitSize;
6532 return GetOrCreatePhysicalRegisterOperand(useFpReg ? V0 : R0, bitSize, GetRegTyFromPrimTy(primType));
6533 }
6534
CreateRegisterOperandOfType(PrimType primType)6535 RegOperand &AArch64CGFunc::CreateRegisterOperandOfType(PrimType primType)
6536 {
6537 RegType regType = GetRegTyFromPrimTy(primType);
6538 uint32 byteLength = GetPrimTypeSize(primType);
6539 return CreateRegisterOperandOfType(regType, byteLength);
6540 }
6541
CreateRegisterOperandOfType(RegType regty,uint32 byteLen)6542 RegOperand &AArch64CGFunc::CreateRegisterOperandOfType(RegType regty, uint32 byteLen)
6543 {
6544 /* BUG: if half-precision floating point operations are supported? */
6545 /* AArch64 has 32-bit and 64-bit registers only */
6546 if (byteLen < k4ByteSize) {
6547 byteLen = k4ByteSize;
6548 }
6549 regno_t vRegNO = NewVReg(regty, byteLen);
6550 return CreateVirtualRegisterOperand(vRegNO);
6551 }
6552
CreateRflagOperand()6553 RegOperand &AArch64CGFunc::CreateRflagOperand()
6554 {
6555 /* AArch64 has Status register that is 32-bit wide. */
6556 regno_t vRegNO = NewVRflag();
6557 return CreateVirtualRegisterOperand(vRegNO);
6558 }
6559
MergeReturn()6560 void AArch64CGFunc::MergeReturn()
6561 {
6562 DEBUG_ASSERT(GetCurBB()->GetPrev()->GetFirstStmt() == GetCleanupLabel(), "must be");
6563
6564 uint32 exitBBSize = GetExitBBsVec().size();
6565 if (exitBBSize == 0) {
6566 return;
6567 }
6568 if ((exitBBSize == 1) && GetExitBB(0) == GetCurBB()) {
6569 return;
6570 }
6571 if (exitBBSize == 1) {
6572 BB *onlyExitBB = GetExitBB(0);
6573 BB *onlyExitBBNext = onlyExitBB->GetNext();
6574 StmtNode *stmt = onlyExitBBNext->GetFirstStmt();
6575 /* only deal with the return_BB in the middle */
6576 if (stmt != GetCleanupLabel()) {
6577 LabelIdx labidx = CreateLabel();
6578 BB *retBB = CreateNewBB(labidx, onlyExitBB->IsUnreachable(), BB::kBBReturn, onlyExitBB->GetFrequency());
6579 onlyExitBB->AppendBB(*retBB);
6580 /* modify the original return BB. */
6581 DEBUG_ASSERT(onlyExitBB->GetKind() == BB::kBBReturn, "Error: suppose to merge multi return bb");
6582 onlyExitBB->SetKind(BB::kBBFallthru);
6583
6584 GetExitBBsVec().pop_back();
6585 GetExitBBsVec().emplace_back(retBB);
6586 return;
6587 }
6588 }
6589
6590 LabelIdx labidx = CreateLabel();
6591 LabelOperand &targetOpnd = GetOrCreateLabelOperand(labidx);
6592 uint32 freq = 0;
6593 for (auto *tmpBB : GetExitBBsVec()) {
6594 DEBUG_ASSERT(tmpBB->GetKind() == BB::kBBReturn, "Error: suppose to merge multi return bb");
6595 tmpBB->SetKind(BB::kBBGoto);
6596 tmpBB->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xuncond, targetOpnd));
6597 freq += tmpBB->GetFrequency();
6598 }
6599 BB *retBB = CreateNewBB(labidx, false, BB::kBBReturn, freq);
6600 GetCleanupBB()->PrependBB(*retBB);
6601
6602 GetExitBBsVec().clear();
6603 GetExitBBsVec().emplace_back(retBB);
6604 }
6605
HandleRetCleanup(NaryStmtNode & retNode)6606 void AArch64CGFunc::HandleRetCleanup(NaryStmtNode &retNode)
6607 {
6608 if (!GetCG()->GenLocalRC()) {
6609 /* handle local rc is disabled. */
6610 return;
6611 }
6612
6613 constexpr uint8 opSize = 11;
6614 Opcode ops[opSize] = {OP_label, OP_goto, OP_brfalse, OP_brtrue, OP_return, OP_call,
6615 OP_icall, OP_rangegoto, OP_catch, OP_try, OP_endtry};
6616 std::set<Opcode> branchOp(ops, ops + opSize);
6617
6618 /* get cleanup intrinsic */
6619 bool found = false;
6620 StmtNode *cleanupNode = retNode.GetPrev();
6621 cleanEANode = nullptr;
6622 while (cleanupNode != nullptr) {
6623 if (branchOp.find(cleanupNode->GetOpCode()) != branchOp.end()) {
6624 if (cleanupNode->GetOpCode() == OP_call) {
6625 CallNode *callNode = static_cast<CallNode *>(cleanupNode);
6626 MIRFunction *fn = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(callNode->GetPUIdx());
6627 MIRSymbol *fsym = GetFunction().GetLocalOrGlobalSymbol(fn->GetStIdx(), false);
6628 if ((fsym->GetName() == "MCC_DecRef_NaiveRCFast") || (fsym->GetName() == "MCC_IncRef_NaiveRCFast") ||
6629 (fsym->GetName() == "MCC_IncDecRef_NaiveRCFast") || (fsym->GetName() == "MCC_LoadRefStatic") ||
6630 (fsym->GetName() == "MCC_LoadRefField") || (fsym->GetName() == "MCC_LoadReferentField") ||
6631 (fsym->GetName() == "MCC_LoadRefField_NaiveRCFast") ||
6632 (fsym->GetName() == "MCC_LoadVolatileField") ||
6633 (fsym->GetName() == "MCC_LoadVolatileStaticField") || (fsym->GetName() == "MCC_LoadWeakField") ||
6634 (fsym->GetName() == "MCC_CheckObjMem")) {
6635 cleanupNode = cleanupNode->GetPrev();
6636 continue;
6637 } else {
6638 break;
6639 }
6640 } else {
6641 break;
6642 }
6643 }
6644
6645 cleanupNode = cleanupNode->GetPrev();
6646 }
6647
6648 if (!found) {
6649 MIRSymbol *retRef = nullptr;
6650 if (retNode.NumOpnds() != 0) {
6651 retRef = GetRetRefSymbol(*static_cast<NaryStmtNode &>(retNode).Opnd(0));
6652 }
6653 HandleRCCall(false, retRef);
6654 }
6655 }
6656
GenRetCleanup(const IntrinsiccallNode * cleanupNode,bool forEA)6657 bool AArch64CGFunc::GenRetCleanup(const IntrinsiccallNode *cleanupNode, bool forEA)
6658 {
6659 #undef CC_DEBUG_INFO
6660
6661 #ifdef CC_DEBUG_INFO
6662 LogInfo::MapleLogger() << "==============" << GetFunction().GetName() << "==============" << '\n';
6663 #endif
6664
6665 if (cleanupNode == nullptr) {
6666 return false;
6667 }
6668
6669 int32 minByteOffset = INT_MAX;
6670 int32 maxByteOffset = 0;
6671
6672 int32 skipIndex = -1;
6673 MIRSymbol *skipSym = nullptr;
6674 size_t refSymNum = 0;
6675
6676 /* now compute the offset range */
6677 std::vector<int32> offsets;
6678 AArch64MemLayout *memLayout = static_cast<AArch64MemLayout *>(this->GetMemlayout());
6679 for (size_t i = 0; i < refSymNum; ++i) {
6680 BaseNode *argExpr = cleanupNode->Opnd(i);
6681 CHECK_FATAL(argExpr->GetOpCode() == OP_dread, "should be dread");
6682 DreadNode *refNode = static_cast<DreadNode *>(argExpr);
6683 MIRSymbol *refSymbol = GetFunction().GetLocalOrGlobalSymbol(refNode->GetStIdx());
6684 if (memLayout->GetSymAllocTable().size() <= refSymbol->GetStIndex()) {
6685 ERR(kLncErr, "access memLayout->GetSymAllocTable() failed");
6686 return false;
6687 }
6688 AArch64SymbolAlloc *symLoc =
6689 static_cast<AArch64SymbolAlloc *>(memLayout->GetSymAllocInfo(refSymbol->GetStIndex()));
6690 int32 tempOffset = GetBaseOffset(*symLoc);
6691 offsets.emplace_back(tempOffset);
6692 #ifdef CC_DEBUG_INFO
6693 LogInfo::MapleLogger() << "refsym " << refSymbol->GetName() << " offset " << tempOffset << '\n';
6694 #endif
6695 minByteOffset = (minByteOffset > tempOffset) ? tempOffset : minByteOffset;
6696 maxByteOffset = (maxByteOffset < tempOffset) ? tempOffset : maxByteOffset;
6697 }
6698
6699 /* get the skip offset */
6700 int32 skipOffset = -1;
6701 if (skipSym != nullptr) {
6702 AArch64SymbolAlloc *symLoc =
6703 static_cast<AArch64SymbolAlloc *>(memLayout->GetSymAllocInfo(skipSym->GetStIndex()));
6704 CHECK_FATAL(GetBaseOffset(*symLoc) < std::numeric_limits<int32>::max(), "out of range");
6705 skipOffset = GetBaseOffset(*symLoc);
6706 offsets.emplace_back(skipOffset);
6707
6708 #ifdef CC_DEBUG_INFO
6709 LogInfo::MapleLogger() << "skip " << skipSym->GetName() << " offset " << skipOffset << '\n';
6710 #endif
6711
6712 skipIndex = symLoc->GetOffset() / kAarch64OffsetAlign;
6713 }
6714
6715 /* call runtime cleanup */
6716 if (minByteOffset < INT_MAX) {
6717 int32 refLocBase = memLayout->GetRefLocBaseLoc();
6718 uint32 refNum = memLayout->GetSizeOfRefLocals() / kAarch64OffsetAlign;
6719 CHECK_FATAL((static_cast<uint32_t>(refLocBase) + (refNum - 1) * kAarch64IntregBytelen) <
6720 static_cast<uint32_t>(std::numeric_limits<int32>::max()), "out of range");
6721 int32 refLocEnd = refLocBase + static_cast<int32>((refNum - 1) * kAarch64IntregBytelen);
6722 int32 realMin = minByteOffset < refLocBase ? refLocBase : minByteOffset;
6723 int32 realMax = maxByteOffset > refLocEnd ? refLocEnd : maxByteOffset;
6724 if (forEA) {
6725 std::sort(offsets.begin(), offsets.end());
6726 int32 prev = offsets[0];
6727 for (size_t i = 1; i < offsets.size(); i++) {
6728 CHECK_FATAL((offsets[i] == prev) || ((offsets[i] - prev) == kAarch64IntregBytelen), "must be");
6729 prev = offsets[i];
6730 }
6731 CHECK_FATAL((refLocBase - prev) == kAarch64IntregBytelen, "must be");
6732 realMin = minByteOffset;
6733 realMax = maxByteOffset;
6734 }
6735 #ifdef CC_DEBUG_INFO
6736 LogInfo::MapleLogger() << " realMin " << realMin << " realMax " << realMax << '\n';
6737 #endif
6738 if (realMax < realMin) {
6739 /* maybe there is a cleanup intrinsic bug, use CHECK_FATAL instead? */
6740 CHECK_FATAL(false, "must be");
6741 }
6742
6743 /* optimization for little slot cleanup */
6744 if (realMax == realMin && !forEA) {
6745 RegOperand &phyOpnd = GetOrCreatePhysicalRegisterOperand(R0, k64BitSize, GetRegTyFromPrimTy(PTY_a64));
6746 Operand &stackLoc = CreateStkTopOpnd(static_cast<uint32>(realMin), GetPointerSize() * kBitsPerByte);
6747 Insn &ldrInsn = GetInsnBuilder()->BuildInsn(PickLdInsn(k64BitSize, PTY_a64), phyOpnd, stackLoc);
6748 GetCurBB()->AppendInsn(ldrInsn);
6749
6750 ListOperand *srcOpnds = CreateListOpnd(*GetFuncScopeAllocator());
6751 srcOpnds->PushOpnd(phyOpnd);
6752 MIRSymbol *callSym = GlobalTables::GetGsymTable().CreateSymbol(kScopeGlobal);
6753 std::string funcName("MCC_DecRef_NaiveRCFast");
6754 callSym->SetNameStrIdx(funcName);
6755 callSym->SetStorageClass(kScText);
6756 callSym->SetSKind(kStFunc);
6757 Insn &callInsn = AppendCall(*callSym, *srcOpnds);
6758 callInsn.SetRefSkipIdx(skipIndex);
6759 GetCurBB()->SetHasCall();
6760 /* because of return stmt is often the last stmt */
6761 GetCurBB()->SetFrequency(frequency);
6762
6763 return true;
6764 }
6765 ListOperand *srcOpnds = CreateListOpnd(*GetFuncScopeAllocator());
6766
6767 ImmOperand &beginOpnd = CreateImmOperand(realMin, k64BitSize, true);
6768 regno_t vRegNO0 = NewVReg(GetRegTyFromPrimTy(PTY_a64), GetPrimTypeSize(PTY_a64));
6769 RegOperand &vReg0 = CreateVirtualRegisterOperand(vRegNO0);
6770 RegOperand &fpOpnd = GetOrCreateStackBaseRegOperand();
6771 SelectAdd(vReg0, fpOpnd, beginOpnd, PTY_i64);
6772
6773 RegOperand &parmRegOpnd1 = GetOrCreatePhysicalRegisterOperand(R0, k64BitSize, GetRegTyFromPrimTy(PTY_a64));
6774 srcOpnds->PushOpnd(parmRegOpnd1);
6775 SelectCopy(parmRegOpnd1, PTY_a64, vReg0, PTY_a64);
6776
6777 uint32 realRefNum = static_cast<uint32>((realMax - realMin) / kAarch64OffsetAlign + 1);
6778
6779 ImmOperand &countOpnd = CreateImmOperand(realRefNum, k64BitSize, true);
6780
6781 RegOperand &parmRegOpnd2 = GetOrCreatePhysicalRegisterOperand(R1, k64BitSize, GetRegTyFromPrimTy(PTY_a64));
6782 srcOpnds->PushOpnd(parmRegOpnd2);
6783 SelectCopyImm(parmRegOpnd2, countOpnd, PTY_i64);
6784
6785 MIRSymbol *funcSym = GlobalTables::GetGsymTable().CreateSymbol(kScopeGlobal);
6786 if ((skipSym != nullptr) && (skipOffset >= realMin) && (skipOffset <= realMax)) {
6787 /* call cleanupskip */
6788 uint32 stOffset = (skipOffset - realMin) / kAarch64OffsetAlign;
6789 ImmOperand &retLoc = CreateImmOperand(stOffset, k64BitSize, true);
6790
6791 RegOperand &parmRegOpnd3 = GetOrCreatePhysicalRegisterOperand(R2, k64BitSize, GetRegTyFromPrimTy(PTY_a64));
6792 srcOpnds->PushOpnd(parmRegOpnd3);
6793 SelectCopyImm(parmRegOpnd3, retLoc, PTY_i64);
6794
6795 std::string funcName;
6796 if (forEA) {
6797 funcName = "MCC_CleanupNonRetEscObj";
6798 } else {
6799 funcName = "MCC_CleanupLocalStackRefSkip_NaiveRCFast";
6800 }
6801 funcSym->SetNameStrIdx(funcName);
6802 #ifdef CC_DEBUG_INFO
6803 LogInfo::MapleLogger() << "num " << real_ref_num << " skip loc " << stOffset << '\n';
6804 #endif
6805 } else {
6806 /* call cleanup */
6807 CHECK_FATAL(!forEA, "must be");
6808 std::string funcName("MCC_CleanupLocalStackRef_NaiveRCFast");
6809 funcSym->SetNameStrIdx(funcName);
6810 #ifdef CC_DEBUG_INFO
6811 LogInfo::MapleLogger() << "num " << real_ref_num << '\n';
6812 #endif
6813 }
6814
6815 funcSym->SetStorageClass(kScText);
6816 funcSym->SetSKind(kStFunc);
6817 Insn &callInsn = AppendCall(*funcSym, *srcOpnds);
6818 callInsn.SetRefSkipIdx(skipIndex);
6819 GetCurBB()->SetHasCall();
6820 GetCurBB()->SetFrequency(frequency);
6821 }
6822 return true;
6823 }
6824
CreateVirtualRegisterOperand(regno_t vRegNO,uint32 size,RegType kind,uint32 flg) const6825 RegOperand *AArch64CGFunc::CreateVirtualRegisterOperand(regno_t vRegNO, uint32 size, RegType kind, uint32 flg) const
6826 {
6827 RegOperand *res = memPool->New<RegOperand>(vRegNO, size, kind, flg);
6828 maplebe::VregInfo::vRegOperandTable[vRegNO] = res;
6829 return res;
6830 }
6831
CreateVirtualRegisterOperand(regno_t vRegNO)6832 RegOperand &AArch64CGFunc::CreateVirtualRegisterOperand(regno_t vRegNO)
6833 {
6834 DEBUG_ASSERT((vReg.vRegOperandTable.find(vRegNO) == vReg.vRegOperandTable.end()), "already exist");
6835 DEBUG_ASSERT(vRegNO < vReg.VRegTableSize(), "index out of range");
6836 uint8 bitSize = static_cast<uint8>((static_cast<uint32>(vReg.VRegTableGetSize(vRegNO))) * kBitsPerByte);
6837 RegOperand *res = CreateVirtualRegisterOperand(vRegNO, bitSize, vReg.VRegTableGetType(vRegNO));
6838 return *res;
6839 }
6840
GetOrCreateVirtualRegisterOperand(regno_t vRegNO)6841 RegOperand &AArch64CGFunc::GetOrCreateVirtualRegisterOperand(regno_t vRegNO)
6842 {
6843 auto it = maplebe::VregInfo::vRegOperandTable.find(vRegNO);
6844 return (it != maplebe::VregInfo::vRegOperandTable.end()) ? *(it->second) : CreateVirtualRegisterOperand(vRegNO);
6845 }
6846
GetOrCreateVirtualRegisterOperand(RegOperand & regOpnd)6847 RegOperand &AArch64CGFunc::GetOrCreateVirtualRegisterOperand(RegOperand ®Opnd)
6848 {
6849 regno_t regNO = regOpnd.GetRegisterNumber();
6850 auto it = maplebe::VregInfo::vRegOperandTable.find(regNO);
6851 if (it != maplebe::VregInfo::vRegOperandTable.end()) {
6852 it->second->SetSize(regOpnd.GetSize());
6853 it->second->SetRegisterNumber(regNO);
6854 it->second->SetRegisterType(regOpnd.GetRegisterType());
6855 it->second->SetValidBitsNum(regOpnd.GetValidBitsNum());
6856 return *it->second;
6857 } else {
6858 auto *newRegOpnd = static_cast<RegOperand *>(regOpnd.Clone(*memPool));
6859 regno_t newRegNO = newRegOpnd->GetRegisterNumber();
6860 if (newRegNO >= GetMaxRegNum()) {
6861 SetMaxRegNum(newRegNO + kRegIncrStepLen);
6862 vReg.VRegTableResize(GetMaxRegNum());
6863 }
6864 maplebe::VregInfo::vRegOperandTable[newRegNO] = newRegOpnd;
6865 VirtualRegNode *vregNode = memPool->New<VirtualRegNode>(newRegOpnd->GetRegisterType(), newRegOpnd->GetSize());
6866 vReg.VRegTableElementSet(newRegNO, vregNode);
6867 vReg.SetCount(GetMaxRegNum());
6868 return *newRegOpnd;
6869 }
6870 }
6871
HandleRCCall(bool begin,const MIRSymbol * retRef)6872 void AArch64CGFunc::HandleRCCall(bool begin, const MIRSymbol *retRef)
6873 {
6874 if (!GetCG()->GenLocalRC() && !begin) {
6875 /* handle local rc is disabled. */
6876 return;
6877 }
6878
6879 AArch64MemLayout *memLayout = static_cast<AArch64MemLayout *>(this->GetMemlayout());
6880 int32 refNum = static_cast<int32>(memLayout->GetSizeOfRefLocals() / kAarch64OffsetAlign);
6881 if (!refNum) {
6882 if (begin) {
6883 GenerateYieldpoint(*GetCurBB());
6884 yieldPointInsn = GetCurBB()->GetLastInsn();
6885 }
6886 return;
6887 }
6888
6889 /* no MCC_CleanupLocalStackRefSkip when ret_ref is the only ref symbol */
6890 if ((refNum == 1) && (retRef != nullptr)) {
6891 if (begin) {
6892 GenerateYieldpoint(*GetCurBB());
6893 yieldPointInsn = GetCurBB()->GetLastInsn();
6894 }
6895 return;
6896 }
6897 CHECK_FATAL(refNum < 0xFFFF, "not enough room for size.");
6898 int32 refLocBase = memLayout->GetRefLocBaseLoc();
6899 CHECK_FATAL((refLocBase >= 0) && (refLocBase < 0xFFFF), "not enough room for offset.");
6900 int32 formalRef = 0;
6901 /* avoid store zero to formal localrefvars. */
6902 if (begin) {
6903 for (uint32 i = 0; i < GetFunction().GetFormalCount(); ++i) {
6904 if (GetFunction().GetNthParamAttr(i).GetAttr(ATTR_localrefvar)) {
6905 refNum--;
6906 formalRef++;
6907 }
6908 }
6909 }
6910 /*
6911 * if the number of local refvar is less than 12, use stp or str to init local refvar
6912 * else call function MCC_InitializeLocalStackRef to init.
6913 */
6914 if (begin && (refNum <= kRefNum12) &&
6915 ((refLocBase + kAarch64IntregBytelen * (refNum - 1)) < kStpLdpImm64UpperBound)) {
6916 int32 pairNum = refNum / kDivide2;
6917 int32 singleNum = refNum % kDivide2;
6918 const int32 pairRefBytes = 16; /* the size of each pair of ref is 16 bytes */
6919 int32 ind = 0;
6920 while (ind < pairNum) {
6921 int32 offset = memLayout->GetRefLocBaseLoc() + kAarch64IntregBytelen * formalRef + pairRefBytes * ind;
6922 Operand &zeroOp = GetZeroOpnd(k64BitSize);
6923 Operand &stackLoc = CreateStkTopOpnd(static_cast<uint32>(offset), GetPointerSize() * kBitsPerByte);
6924 Insn &setInc = GetInsnBuilder()->BuildInsn(MOP_xstp, zeroOp, zeroOp, stackLoc);
6925 GetCurBB()->AppendInsn(setInc);
6926 ind++;
6927 }
6928 if (singleNum > 0) {
6929 int32 offset = memLayout->GetRefLocBaseLoc() + kAarch64IntregBytelen * formalRef +
6930 kAarch64IntregBytelen * (refNum - 1);
6931 Operand &zeroOp = GetZeroOpnd(k64BitSize);
6932 Operand &stackLoc = CreateStkTopOpnd(static_cast<uint32>(offset), GetPointerSize() * kBitsPerByte);
6933 Insn &setInc = GetInsnBuilder()->BuildInsn(MOP_xstr, zeroOp, stackLoc);
6934 GetCurBB()->AppendInsn(setInc);
6935 }
6936 /* Insert Yield Point just after localrefvar are initialized. */
6937 GenerateYieldpoint(*GetCurBB());
6938 yieldPointInsn = GetCurBB()->GetLastInsn();
6939 return;
6940 }
6941
6942 /* refNum is 1 and refvar is not returned, this refvar need to call MCC_DecRef_NaiveRCFast. */
6943 if ((refNum == 1) && !begin && (retRef == nullptr)) {
6944 RegOperand &phyOpnd = GetOrCreatePhysicalRegisterOperand(R0, k64BitSize, GetRegTyFromPrimTy(PTY_a64));
6945 Operand &stackLoc =
6946 CreateStkTopOpnd(static_cast<uint32>(memLayout->GetRefLocBaseLoc()), GetPointerSize() * kBitsPerByte);
6947 Insn &ldrInsn = GetInsnBuilder()->BuildInsn(PickLdInsn(k64BitSize, PTY_a64), phyOpnd, stackLoc);
6948 GetCurBB()->AppendInsn(ldrInsn);
6949
6950 ListOperand *srcOpnds = CreateListOpnd(*GetFuncScopeAllocator());
6951 srcOpnds->PushOpnd(phyOpnd);
6952 MIRSymbol *callSym = GlobalTables::GetGsymTable().CreateSymbol(kScopeGlobal);
6953 std::string funcName("MCC_DecRef_NaiveRCFast");
6954 callSym->SetNameStrIdx(funcName);
6955 callSym->SetStorageClass(kScText);
6956 callSym->SetSKind(kStFunc);
6957
6958 AppendCall(*callSym, *srcOpnds);
6959 GetCurBB()->SetHasCall();
6960 if (frequency != 0) {
6961 GetCurBB()->SetFrequency(frequency);
6962 }
6963 return;
6964 }
6965
6966 /* refNum is 2 and one of refvar is returned, only another one is needed to call MCC_DecRef_NaiveRCFast. */
6967 if ((refNum == 2) && !begin && retRef != nullptr) {
6968 AArch64SymbolAlloc *symLoc =
6969 static_cast<AArch64SymbolAlloc *>(memLayout->GetSymAllocInfo(retRef->GetStIndex()));
6970 int32 stOffset = symLoc->GetOffset() / kAarch64OffsetAlign;
6971 RegOperand &phyOpnd = GetOrCreatePhysicalRegisterOperand(R0, k64BitSize, GetRegTyFromPrimTy(PTY_a64));
6972 Operand *stackLoc = nullptr;
6973 if (stOffset == 0) {
6974 /* just have to Dec the next one. */
6975 stackLoc = &CreateStkTopOpnd(static_cast<uint32>(memLayout->GetRefLocBaseLoc()) + kAarch64IntregBytelen,
6976 GetPointerSize() * kBitsPerByte);
6977 } else {
6978 /* just have to Dec the current one. */
6979 stackLoc =
6980 &CreateStkTopOpnd(static_cast<uint32>(memLayout->GetRefLocBaseLoc()), GetPointerSize() * kBitsPerByte);
6981 }
6982 Insn &ldrInsn = GetInsnBuilder()->BuildInsn(PickLdInsn(k64BitSize, PTY_a64), phyOpnd, *stackLoc);
6983 GetCurBB()->AppendInsn(ldrInsn);
6984
6985 ListOperand *srcOpnds = CreateListOpnd(*GetFuncScopeAllocator());
6986 srcOpnds->PushOpnd(phyOpnd);
6987 MIRSymbol *callSym = GlobalTables::GetGsymTable().CreateSymbol(kScopeGlobal);
6988 std::string funcName("MCC_DecRef_NaiveRCFast");
6989 callSym->SetNameStrIdx(funcName);
6990 callSym->SetStorageClass(kScText);
6991 callSym->SetSKind(kStFunc);
6992 Insn &callInsn = AppendCall(*callSym, *srcOpnds);
6993 callInsn.SetRefSkipIdx(stOffset);
6994 GetCurBB()->SetHasCall();
6995 if (frequency != 0) {
6996 GetCurBB()->SetFrequency(frequency);
6997 }
6998 return;
6999 }
7000
7001 bool needSkip = false;
7002 ListOperand *srcOpnds = CreateListOpnd(*GetFuncScopeAllocator());
7003
7004 ImmOperand *beginOpnd =
7005 &CreateImmOperand(memLayout->GetRefLocBaseLoc() + kAarch64IntregBytelen * formalRef, k64BitSize, true);
7006 ImmOperand *countOpnd = &CreateImmOperand(refNum, k64BitSize, true);
7007 int32 refSkipIndex = -1;
7008 if (!begin && retRef != nullptr) {
7009 AArch64SymbolAlloc *symLoc =
7010 static_cast<AArch64SymbolAlloc *>(memLayout->GetSymAllocInfo(retRef->GetStIndex()));
7011 int32 stOffset = symLoc->GetOffset() / kAarch64OffsetAlign;
7012 refSkipIndex = stOffset;
7013 if (stOffset == 0) {
7014 /* ret_ref at begin. */
7015 beginOpnd = &CreateImmOperand(memLayout->GetRefLocBaseLoc() + kAarch64IntregBytelen, k64BitSize, true);
7016 countOpnd = &CreateImmOperand(refNum - 1, k64BitSize, true);
7017 } else if (stOffset == (refNum - 1)) {
7018 /* ret_ref at end. */
7019 countOpnd = &CreateImmOperand(refNum - 1, k64BitSize, true);
7020 } else {
7021 needSkip = true;
7022 }
7023 }
7024
7025 regno_t vRegNO0 = NewVReg(GetRegTyFromPrimTy(PTY_a64), GetPrimTypeSize(PTY_a64));
7026 RegOperand &vReg0 = CreateVirtualRegisterOperand(vRegNO0);
7027 RegOperand &fpOpnd = GetOrCreateStackBaseRegOperand();
7028 SelectAdd(vReg0, fpOpnd, *beginOpnd, PTY_i64);
7029
7030 RegOperand &parmRegOpnd1 = GetOrCreatePhysicalRegisterOperand(R0, k64BitSize, GetRegTyFromPrimTy(PTY_a64));
7031 srcOpnds->PushOpnd(parmRegOpnd1);
7032 SelectCopy(parmRegOpnd1, PTY_a64, vReg0, PTY_a64);
7033
7034 regno_t vRegNO1 = NewVReg(GetRegTyFromPrimTy(PTY_a64), GetPrimTypeSize(PTY_a64));
7035 RegOperand &vReg1 = CreateVirtualRegisterOperand(vRegNO1);
7036 SelectCopyImm(vReg1, *countOpnd, PTY_i64);
7037
7038 RegOperand &parmRegOpnd2 = GetOrCreatePhysicalRegisterOperand(R1, k64BitSize, GetRegTyFromPrimTy(PTY_a64));
7039 srcOpnds->PushOpnd(parmRegOpnd2);
7040 SelectCopy(parmRegOpnd2, PTY_a64, vReg1, PTY_a64);
7041
7042 MIRSymbol *sym = GlobalTables::GetGsymTable().CreateSymbol(kScopeGlobal);
7043 if (begin) {
7044 std::string funcName("MCC_InitializeLocalStackRef");
7045 sym->SetNameStrIdx(funcName);
7046 CHECK_FATAL(countOpnd->GetValue() > 0, "refCount should be greater than 0.");
7047 refCount = static_cast<uint32>(countOpnd->GetValue());
7048 beginOffset = beginOpnd->GetValue();
7049 } else if (!needSkip) {
7050 std::string funcName("MCC_CleanupLocalStackRef_NaiveRCFast");
7051 sym->SetNameStrIdx(funcName);
7052 } else {
7053 CHECK_NULL_FATAL(retRef);
7054 if (retRef->GetStIndex() >= memLayout->GetSymAllocTable().size()) {
7055 CHECK_FATAL(false, "index out of range in AArch64CGFunc::HandleRCCall");
7056 }
7057 AArch64SymbolAlloc *symLoc =
7058 static_cast<AArch64SymbolAlloc *>(memLayout->GetSymAllocInfo(retRef->GetStIndex()));
7059 int32 stOffset = symLoc->GetOffset() / kAarch64OffsetAlign;
7060 ImmOperand &retLoc = CreateImmOperand(stOffset, k64BitSize, true);
7061
7062 regno_t vRegNO2 = NewVReg(GetRegTyFromPrimTy(PTY_a64), GetPrimTypeSize(PTY_a64));
7063 RegOperand &vReg2 = CreateVirtualRegisterOperand(vRegNO2);
7064 SelectCopyImm(vReg2, retLoc, PTY_i64);
7065
7066 RegOperand &parmRegOpnd3 = GetOrCreatePhysicalRegisterOperand(R2, k64BitSize, GetRegTyFromPrimTy(PTY_a64));
7067 srcOpnds->PushOpnd(parmRegOpnd3);
7068 SelectCopy(parmRegOpnd3, PTY_a64, vReg2, PTY_a64);
7069
7070 std::string funcName("MCC_CleanupLocalStackRefSkip_NaiveRCFast");
7071 sym->SetNameStrIdx(funcName);
7072 }
7073 sym->SetStorageClass(kScText);
7074 sym->SetSKind(kStFunc);
7075
7076 Insn &callInsn = AppendCall(*sym, *srcOpnds);
7077 callInsn.SetRefSkipIdx(refSkipIndex);
7078 if (frequency != 0) {
7079 GetCurBB()->SetFrequency(frequency);
7080 }
7081 GetCurBB()->SetHasCall();
7082 if (begin) {
7083 /* Insert Yield Point just after localrefvar are initialized. */
7084 GenerateYieldpoint(*GetCurBB());
7085 yieldPointInsn = GetCurBB()->GetLastInsn();
7086 }
7087 }
7088
SelectLibMemCopy(RegOperand & destOpnd,RegOperand & srcOpnd,uint32 structSize)7089 void AArch64CGFunc::SelectLibMemCopy(RegOperand &destOpnd, RegOperand &srcOpnd, uint32 structSize)
7090 {
7091 std::vector<Operand *> opndVec;
7092 opndVec.push_back(&CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize))); // result
7093 opndVec.push_back(&destOpnd); // param 0
7094 opndVec.push_back(&srcOpnd); // param 1
7095 opndVec.push_back(&CreateImmOperand(structSize, k64BitSize, false)); // param 2
7096 SelectLibCall("memcpy", opndVec, PTY_a64, PTY_a64);
7097 }
7098
SelectInsnMemCopy(const MemOperand & destOpnd,const MemOperand & srcOpnd,uint32 size,bool isRefField,BaseNode * destNode,BaseNode * srcNode)7099 void AArch64CGFunc::SelectInsnMemCopy(const MemOperand &destOpnd, const MemOperand &srcOpnd, uint32 size,
7100 bool isRefField, BaseNode *destNode, BaseNode *srcNode)
7101 {
7102 auto createMemCopy = [this, isRefField, destNode, srcNode](MemOperand &dest, MemOperand &src, uint32 byteSize) {
7103 Insn *ldInsn = nullptr;
7104 Insn *stInsn = nullptr;
7105
7106 if (byteSize == k16ByteSize) { // ldp/stp
7107 auto &tmpOpnd1 = CreateRegisterOperandOfType(PTY_u64);
7108 auto &tmpOpnd2 = CreateRegisterOperandOfType(PTY_u64);
7109 // ldr srcMem to tmpReg
7110 ldInsn = &GetInsnBuilder()->BuildInsn(MOP_xldp, tmpOpnd1, tmpOpnd2, src);
7111 // str tempReg to destMem
7112 stInsn = &GetInsnBuilder()->BuildInsn(MOP_xstp, tmpOpnd1, tmpOpnd2, dest);
7113 } else {
7114 PrimType primType =
7115 (byteSize == k8ByteSize)
7116 ? PTY_u64
7117 : (byteSize == k4ByteSize)
7118 ? PTY_u32
7119 : (byteSize == k2ByteSize) ? PTY_u16 : (byteSize == k1ByteSize) ? PTY_u8 : PTY_unknown;
7120 DEBUG_ASSERT(primType != PTY_unknown, "NIY, unkown byte size");
7121 auto &tmpOpnd = CreateRegisterOperandOfType(kRegTyInt, byteSize);
7122 // ldr srcMem to tmpReg
7123 ldInsn = &GetInsnBuilder()->BuildInsn(PickLdInsn(byteSize * kBitsPerByte, primType), tmpOpnd, src);
7124 // str tempReg to destMem
7125 stInsn = &GetInsnBuilder()->BuildInsn(PickStInsn(byteSize * kBitsPerByte, primType), tmpOpnd, dest);
7126 }
7127 // Set memory reference info
7128 SetMemReferenceOfInsn(*ldInsn, srcNode);
7129 ldInsn->MarkAsAccessRefField(isRefField);
7130 GetCurBB()->AppendInsn(*ldInsn);
7131 if (!VERIFY_INSN(ldInsn)) {
7132 SPLIT_INSN(ldInsn, this);
7133 }
7134
7135 // Set memory reference info
7136 SetMemReferenceOfInsn(*stInsn, destNode);
7137 GetCurBB()->AppendInsn(*stInsn);
7138 if (!VERIFY_INSN(stInsn)) {
7139 SPLIT_INSN(stInsn, this);
7140 }
7141 };
7142
7143 // Insn can be reduced when sizeNotCoverd = 3 | 5 | 6 | 7
7144 for (uint32 offset = 0; offset < size;) {
7145 auto sizeNotCoverd = size - offset;
7146 uint32 copyByteSize = 0;
7147 if (destOpnd.GetAddrMode() != MemOperand::kAddrModeLo12Li &&
7148 srcOpnd.GetAddrMode() != MemOperand::kAddrModeLo12Li &&
7149 sizeNotCoverd >= k16ByteSize) { // ldp/stp unsupport lo12li mem
7150 copyByteSize = k16ByteSize;
7151 } else if (sizeNotCoverd >= k8ByteSize) {
7152 copyByteSize = k8ByteSize;
7153 } else if (destOpnd.GetAddrMode() != MemOperand::kAddrModeLo12Li &&
7154 srcOpnd.GetAddrMode() != MemOperand::kAddrModeLo12Li && sizeNotCoverd > k4ByteSize &&
7155 offset >= k8ByteSize - sizeNotCoverd) {
7156 copyByteSize = k8ByteSize; // 5: w + b -> x ; 6: w + h -> x ; 7: w + h + b -> x
7157 offset -= (k8ByteSize - sizeNotCoverd);
7158 } else if (sizeNotCoverd >= k4ByteSize) {
7159 copyByteSize = k4ByteSize;
7160 } else if (destOpnd.GetAddrMode() != MemOperand::kAddrModeLo12Li &&
7161 srcOpnd.GetAddrMode() != MemOperand::kAddrModeLo12Li && sizeNotCoverd > k2ByteSize &&
7162 offset >= k4ByteSize - sizeNotCoverd) {
7163 copyByteSize = k4ByteSize; // 3: h + b -> w
7164 offset -= (k4ByteSize - sizeNotCoverd);
7165 } else if (sizeNotCoverd >= k2ByteSize) {
7166 copyByteSize = k2ByteSize;
7167 } else {
7168 copyByteSize = k1ByteSize;
7169 }
7170
7171 auto &srcMem = GetMemOperandAddOffset(srcOpnd, offset, copyByteSize * kBitsPerByte);
7172 auto &destMem = GetMemOperandAddOffset(destOpnd, offset, copyByteSize * kBitsPerByte);
7173 createMemCopy(destMem, srcMem, copyByteSize);
7174
7175 offset += copyByteSize;
7176 }
7177 }
7178
SelectMemCopy(const MemOperand & destOpnd,const MemOperand & srcOpnd,uint32 size,bool isRefField,BaseNode * destNode,BaseNode * srcNode)7179 void AArch64CGFunc::SelectMemCopy(const MemOperand &destOpnd, const MemOperand &srcOpnd, uint32 size, bool isRefField,
7180 BaseNode *destNode, BaseNode *srcNode)
7181 {
7182 if (size > kParmMemcpySize) {
7183 CHECK_NULL_FATAL(ExtractMemBaseAddr(srcOpnd));
7184 SelectLibMemCopy(*ExtractMemBaseAddr(destOpnd), *ExtractMemBaseAddr(srcOpnd), size);
7185 return;
7186 }
7187 SelectInsnMemCopy(destOpnd, srcOpnd, size, isRefField, destNode, srcNode);
7188 }
7189
SelectParmListPreprocessForAggregate(BaseNode & argExpr,int32 & structCopyOffset,std::vector<ParamDesc> & argsDesc,bool isArgUnused)7190 void AArch64CGFunc::SelectParmListPreprocessForAggregate(BaseNode &argExpr, int32 &structCopyOffset,
7191 std::vector<ParamDesc> &argsDesc, bool isArgUnused)
7192 {
7193 MemRWNodeHelper memHelper(argExpr, GetFunction(), GetBecommon());
7194 auto mirSize = memHelper.GetMemSize();
7195 if (mirSize <= k16BitSize) {
7196 argsDesc.emplace_back(memHelper.GetMIRType(), &argExpr, memHelper.GetByteOffset());
7197 return;
7198 }
7199
7200 PrimType baseType = PTY_begin;
7201 size_t elemNum = 0;
7202 if (IsHomogeneousAggregates(*memHelper.GetMIRType(), baseType, elemNum)) {
7203 // B.3 If the argument type is an HFA or an HVA, then the argument is used unmodified.
7204 argsDesc.emplace_back(memHelper.GetMIRType(), &argExpr, memHelper.GetByteOffset());
7205 return;
7206 }
7207
7208 // B.4 If the argument type is a Composite Type that is larger than 16 bytes,
7209 // then the argument is copied to memory allocated by the caller
7210 // and the argument is replaced by a pointer to the copy.
7211 structCopyOffset = static_cast<int32>(RoundUp(static_cast<uint32>(structCopyOffset), k8ByteSize));
7212
7213 // pre copy
7214 auto *rhsMemOpnd = SelectRhsMemOpnd(argExpr);
7215 if (!isArgUnused) {
7216 auto &spReg = GetOrCreatePhysicalRegisterOperand(RSP, k64BitSize, kRegTyInt);
7217 auto &offsetOpnd = CreateImmOperand(structCopyOffset, k64BitSize, false);
7218 auto *destMemOpnd = CreateMemOperand(k64BitSize, spReg, offsetOpnd, false);
7219
7220 auto copySize = CGOptions::IsBigEndian() ? static_cast<uint32>(RoundUp(mirSize, k8ByteSize)) : mirSize;
7221 SelectMemCopy(*destMemOpnd, *rhsMemOpnd, copySize);
7222 }
7223
7224 (void)argsDesc.emplace_back(memHelper.GetMIRType(), nullptr, static_cast<uint32>(structCopyOffset), true);
7225 structCopyOffset += static_cast<int32>(RoundUp(mirSize, k8ByteSize));
7226 }
7227
7228 // Stage B - Pre-padding and extension of arguments
SelectParmListPreprocess(StmtNode & naryNode,size_t start,std::vector<ParamDesc> & argsDesc,const MIRFunction * callee)7229 bool AArch64CGFunc::SelectParmListPreprocess(StmtNode &naryNode, size_t start, std::vector<ParamDesc> &argsDesc,
7230 const MIRFunction *callee)
7231 {
7232 bool hasSpecialArg = false;
7233 int32 structCopyOffset = GetMaxParamStackSize() - GetStructCopySize();
7234 for (size_t i = start; i < naryNode.NumOpnds(); ++i) {
7235 BaseNode *argExpr = naryNode.Opnd(i);
7236 DEBUG_ASSERT(argExpr != nullptr, "not null check");
7237 PrimType primType = argExpr->GetPrimType();
7238 DEBUG_ASSERT(primType != PTY_void, "primType should not be void");
7239 if (primType == PTY_agg) {
7240 SelectParmListPreprocessForAggregate(*argExpr, structCopyOffset, argsDesc,
7241 (callee && callee->GetFuncDesc().IsArgUnused(i)));
7242 } else {
7243 auto *mirType = GlobalTables::GetTypeTable().GetPrimType(primType);
7244 (void)argsDesc.emplace_back(mirType, argExpr);
7245 }
7246
7247 if (MarkParmListCall(*argExpr)) {
7248 argsDesc.rbegin()->isSpecialArg = true;
7249 hasSpecialArg = true;
7250 }
7251 }
7252 return hasSpecialArg;
7253 }
7254
GetCalleeFunction(StmtNode & naryNode) const7255 std::pair<MIRFunction *, MIRFuncType *> AArch64CGFunc::GetCalleeFunction(StmtNode &naryNode) const
7256 {
7257 MIRFunction *callee = nullptr;
7258 MIRFuncType *calleeType = nullptr;
7259 if (dynamic_cast<CallNode *>(&naryNode) != nullptr) {
7260 auto calleePuIdx = static_cast<CallNode &>(naryNode).GetPUIdx();
7261 callee = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(calleePuIdx);
7262 calleeType = callee->GetMIRFuncType();
7263 } else if (naryNode.GetOpCode() == OP_icallproto) {
7264 auto *iCallNode = &static_cast<IcallNode &>(naryNode);
7265 MIRType *protoType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(iCallNode->GetRetTyIdx());
7266 if (protoType->IsMIRPtrType()) {
7267 calleeType = static_cast<MIRPtrType *>(protoType)->GetPointedFuncType();
7268 } else if (protoType->IsMIRFuncType()) {
7269 calleeType = static_cast<MIRFuncType *>(protoType);
7270 }
7271 }
7272 return {callee, calleeType};
7273 }
7274
SelectParmListSmallStruct(const MIRType & mirType,const CCLocInfo & ploc,Operand & addr,ListOperand & srcOpnds,bool isSpecialArg,std::vector<RegMapForPhyRegCpy> & regMapForTmpBB)7275 void AArch64CGFunc::SelectParmListSmallStruct(const MIRType &mirType, const CCLocInfo &ploc, Operand &addr,
7276 ListOperand &srcOpnds, bool isSpecialArg,
7277 std::vector<RegMapForPhyRegCpy> ®MapForTmpBB)
7278 {
7279 uint32 offset = 0;
7280 DEBUG_ASSERT(addr.IsMemoryAccessOperand(), "NIY, must be mem opnd");
7281 uint64 size = mirType.GetSize();
7282 auto &memOpnd = static_cast<MemOperand &>(addr);
7283 // ldr memOpnd to parmReg
7284 auto loadParamFromMem = [this, &offset, &memOpnd, &srcOpnds, &size, isSpecialArg,
7285 ®MapForTmpBB](AArch64reg regno, PrimType primType) {
7286 auto &phyReg =
7287 GetOrCreatePhysicalRegisterOperand(regno, GetPrimTypeBitSize(primType), GetRegTyFromPrimTy(primType));
7288 bool isFpReg = !IsPrimitiveInteger(primType) || IsPrimitiveVectorFloat(primType);
7289 if (!CGOptions::IsBigEndian() && !isFpReg && (size - offset < k8ByteSize)) {
7290 // load exact size agg (BigEndian not support yet)
7291 RegOperand *valOpnd = nullptr;
7292 for (uint32 exactOfst = 0; exactOfst < (size - offset);) {
7293 PrimType exactPrimType;
7294 auto loadSize = size - offset - exactOfst;
7295 if (loadSize >= k4ByteSize) {
7296 exactPrimType = PTY_u32;
7297 } else if (loadSize >= k2ByteSize) {
7298 exactPrimType = PTY_u16;
7299 } else {
7300 exactPrimType = PTY_u8;
7301 }
7302 auto ldBitSize = GetPrimTypeBitSize(exactPrimType);
7303 auto *ldOpnd = &GetMemOperandAddOffset(memOpnd, exactOfst + offset, ldBitSize);
7304 auto ldMop = PickLdInsn(ldBitSize, exactPrimType);
7305 auto &tmpValOpnd = CreateVirtualRegisterOperand(NewVReg(kRegTyInt, GetPrimTypeSize(exactPrimType)));
7306 Insn &ldInsn = GetInsnBuilder()->BuildInsn(ldMop, tmpValOpnd, *ldOpnd);
7307 GetCurBB()->AppendInsn(ldInsn);
7308 if (!VERIFY_INSN(&ldInsn)) {
7309 SPLIT_INSN(&ldInsn, this);
7310 }
7311
7312 if (exactOfst != 0) {
7313 auto &shiftOpnd = CreateImmOperand(exactOfst * kBitsPerByte, k32BitSize, false);
7314 SelectShift(tmpValOpnd, tmpValOpnd, shiftOpnd, kShiftLeft, primType);
7315 }
7316 if (valOpnd) {
7317 SelectBior(*valOpnd, *valOpnd, tmpValOpnd, primType);
7318 } else {
7319 valOpnd = &tmpValOpnd;
7320 }
7321 exactOfst += GetPrimTypeSize(exactPrimType);
7322 }
7323 if (isSpecialArg) {
7324 regMapForTmpBB.emplace_back(RegMapForPhyRegCpy(&phyReg, primType, valOpnd, primType));
7325 } else {
7326 SelectCopy(phyReg, primType, *valOpnd, primType);
7327 }
7328 } else {
7329 auto *ldOpnd = &GetMemOperandAddOffset(memOpnd, offset, GetPrimTypeBitSize(primType));
7330 auto ldMop = PickLdInsn(GetPrimTypeBitSize(primType), primType);
7331 if (isSpecialArg) {
7332 RegOperand *tmpReg = &CreateRegisterOperandOfType(primType);
7333 Insn &tmpLdInsn = GetInsnBuilder()->BuildInsn(ldMop, *tmpReg, *ldOpnd);
7334 regMapForTmpBB.emplace_back(RegMapForPhyRegCpy(&phyReg, primType, tmpReg, primType));
7335 GetCurBB()->AppendInsn(tmpLdInsn);
7336 if (!VERIFY_INSN(&tmpLdInsn)) {
7337 SPLIT_INSN(&tmpLdInsn, this);
7338 }
7339 } else {
7340 Insn &ldInsn = GetInsnBuilder()->BuildInsn(ldMop, phyReg, *ldOpnd);
7341 GetCurBB()->AppendInsn(ldInsn);
7342 if (!VERIFY_INSN(&ldInsn)) {
7343 SPLIT_INSN(&ldInsn, this);
7344 }
7345 }
7346 }
7347 srcOpnds.PushOpnd(phyReg);
7348 offset += GetPrimTypeSize(primType);
7349 };
7350 loadParamFromMem(static_cast<AArch64reg>(ploc.reg0), ploc.primTypeOfReg0);
7351 if (ploc.reg1 != kRinvalid) {
7352 loadParamFromMem(static_cast<AArch64reg>(ploc.reg1), ploc.primTypeOfReg1);
7353 }
7354 if (ploc.reg2 != kRinvalid) {
7355 loadParamFromMem(static_cast<AArch64reg>(ploc.reg2), ploc.primTypeOfReg2);
7356 }
7357 if (ploc.reg3 != kRinvalid) {
7358 loadParamFromMem(static_cast<AArch64reg>(ploc.reg3), ploc.primTypeOfReg3);
7359 }
7360 }
7361
SelectParmListPassByStack(const MIRType & mirType,Operand & opnd,uint32 memOffset,bool preCopyed,std::vector<Insn * > & insnForStackArgs)7362 void AArch64CGFunc::SelectParmListPassByStack(const MIRType &mirType, Operand &opnd, uint32 memOffset, bool preCopyed,
7363 std::vector<Insn *> &insnForStackArgs)
7364 {
7365 if (!preCopyed && mirType.GetPrimType() == PTY_agg) {
7366 DEBUG_ASSERT(opnd.IsMemoryAccessOperand(), "NIY, must be mem opnd");
7367 auto &actOpnd = CreateMemOpnd(RSP, memOffset, k64BitSize);
7368 auto mirSize = CGOptions::IsBigEndian() ? static_cast<uint32>(RoundUp(mirType.GetSize(), k8ByteSize))
7369 : static_cast<uint32>(mirType.GetSize());
7370 // can't use libmemcpy, it will modify parm reg
7371 SelectInsnMemCopy(actOpnd, static_cast<MemOperand &>(opnd), mirSize);
7372 return;
7373 }
7374
7375 if (IsInt128Ty(mirType.GetPrimType())) {
7376 DEBUG_ASSERT(!preCopyed, "NIY");
7377 MemOperand &mem = CreateMemOpnd(RSP, memOffset, GetPrimTypeBitSize(PTY_u128));
7378 mem.SetStackArgMem(true);
7379 SelectCopy(mem, PTY_u128, opnd, PTY_u128);
7380 return;
7381 }
7382
7383 PrimType primType = preCopyed ? PTY_a64 : mirType.GetPrimType();
7384 CHECK_FATAL(primType != PTY_i128 && primType != PTY_u128, "NIY, i128 is unsupported");
7385 auto &valReg = LoadIntoRegister(opnd, primType);
7386 auto &actMemOpnd = CreateMemOpnd(RSP, memOffset, GetPrimTypeBitSize(primType));
7387 Insn &strInsn = GetInsnBuilder()->BuildInsn(PickStInsn(GetPrimTypeBitSize(primType), primType), valReg, actMemOpnd);
7388 actMemOpnd.SetStackArgMem(true);
7389 if (Globals::GetInstance()->GetOptimLevel() == CGOptions::kLevel2 && insnForStackArgs.size() < kShiftAmount12) {
7390 (void)insnForStackArgs.emplace_back(&strInsn);
7391 } else {
7392 GetCurBB()->AppendInsn(strInsn);
7393 }
7394 }
7395
7396 /* preprocess call in parmlist */
MarkParmListCall(BaseNode & expr)7397 bool AArch64CGFunc::MarkParmListCall(BaseNode &expr)
7398 {
7399 if (!CGOptions::IsPIC()) {
7400 return false;
7401 }
7402 switch (expr.GetOpCode()) {
7403 case OP_addrof: {
7404 auto &addrNode = static_cast<AddrofNode &>(expr);
7405 MIRSymbol *symbol = GetFunction().GetLocalOrGlobalSymbol(addrNode.GetStIdx());
7406 if (symbol->IsThreadLocal()) {
7407 return true;
7408 }
7409 break;
7410 }
7411 default: {
7412 for (auto i = 0; i < expr.GetNumOpnds(); i++) {
7413 if (expr.Opnd(i)) {
7414 if (MarkParmListCall(*expr.Opnd(i))) {
7415 return true;
7416 }
7417 }
7418 }
7419 break;
7420 }
7421 }
7422 return false;
7423 }
7424
7425 /*
7426 SelectParmList generates an instrunction for each of the parameters
7427 to load the parameter value into the corresponding register.
7428 We return a list of registers to the call instruction because
7429 they may be needed in the register allocation phase.
7430 */
SelectParmList(StmtNode & naryNode,ListOperand & srcOpnds,bool isCallNative)7431 void AArch64CGFunc::SelectParmList(StmtNode &naryNode, ListOperand &srcOpnds, bool isCallNative)
7432 {
7433 size_t opndIdx = 0;
7434 // the first opnd of ICallNode is not parameter of function
7435 if (naryNode.GetOpCode() == OP_icall || naryNode.GetOpCode() == OP_icallproto || isCallNative) {
7436 opndIdx++;
7437 }
7438 auto [callee, calleeType] = GetCalleeFunction(naryNode);
7439
7440 std::vector<ParamDesc> argsDesc;
7441 std::vector<RegMapForPhyRegCpy> regMapForTmpBB;
7442 bool hasSpecialArg = SelectParmListPreprocess(naryNode, opndIdx, argsDesc, callee);
7443 BB *curBBrecord = GetCurBB();
7444 BB *tmpBB = nullptr;
7445 if (hasSpecialArg) {
7446 tmpBB = CreateNewBB();
7447 }
7448
7449 AArch64CallConvImpl parmLocator(GetBecommon());
7450 CCLocInfo ploc;
7451 std::vector<Insn *> insnForStackArgs;
7452
7453 for (size_t i = 0; i < argsDesc.size(); ++i) {
7454 if (hasSpecialArg) {
7455 DEBUG_ASSERT(tmpBB, "need temp bb for lower priority args");
7456 SetCurBB(argsDesc[i].isSpecialArg ? *curBBrecord : *tmpBB);
7457 }
7458
7459 auto *mirType = argsDesc[i].mirType;
7460
7461 // get param opnd, for unpreCody agg, opnd must be mem opnd
7462 Operand *opnd = nullptr;
7463 auto preCopyed = argsDesc[i].preCopyed;
7464 if (preCopyed) { // preCopyed agg, passed by address
7465 naryNode.SetMayTailcall(false); // has preCopyed arguments, don't do tailcall
7466 opnd = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
7467 auto &spReg = GetOrCreatePhysicalRegisterOperand(RSP, k64BitSize, kRegTyInt);
7468 SelectAdd(*opnd, spReg, CreateImmOperand(argsDesc[i].offset, k64BitSize, false), PTY_a64);
7469 } else if (mirType->GetPrimType() == PTY_agg) {
7470 opnd = SelectRhsMemOpnd(*argsDesc[i].argExpr);
7471 } else { // base type, clac true val
7472 opnd = &LoadIntoRegister(*HandleExpr(naryNode, *argsDesc[i].argExpr), mirType->GetPrimType());
7473 }
7474 parmLocator.LocateNextParm(*mirType, ploc, (i == 0), calleeType);
7475
7476 // skip unused args
7477 if (callee && callee->GetFuncDesc().IsArgUnused(i)) {
7478 continue;
7479 }
7480
7481 if (ploc.reg0 != kRinvalid) { // load to the register.
7482 if (mirType->GetPrimType() == PTY_agg && !preCopyed) {
7483 SelectParmListSmallStruct(*mirType, ploc, *opnd, srcOpnds, argsDesc[i].isSpecialArg, regMapForTmpBB);
7484 } else if (IsInt128Ty(mirType->GetPrimType())) {
7485 SelectParmListForInt128(*opnd, srcOpnds, ploc, argsDesc[i].isSpecialArg, regMapForTmpBB);
7486 } else {
7487 CHECK_FATAL(ploc.reg1 == kRinvalid, "NIY");
7488 auto &phyReg = GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(ploc.reg0),
7489 GetPrimTypeBitSize(ploc.primTypeOfReg0),
7490 GetRegTyFromPrimTy(ploc.primTypeOfReg0));
7491 DEBUG_ASSERT(opnd->IsRegister(), "NIY, must be reg");
7492 if (!DoCallerEnsureValidParm(phyReg, static_cast<RegOperand &>(*opnd), ploc.primTypeOfReg0)) {
7493 if (argsDesc[i].isSpecialArg) {
7494 regMapForTmpBB.emplace_back(RegMapForPhyRegCpy(
7495 &phyReg, ploc.primTypeOfReg0, static_cast<RegOperand *>(opnd), ploc.primTypeOfReg0));
7496 } else {
7497 SelectCopy(phyReg, ploc.primTypeOfReg0, *opnd, ploc.primTypeOfReg0);
7498 }
7499 }
7500 srcOpnds.PushOpnd(phyReg);
7501 }
7502 continue;
7503 }
7504
7505 // store to the memory segment for stack-passsed arguments.
7506 if (CGOptions::IsBigEndian() && ploc.memSize < static_cast<int32>(k8ByteSize)) {
7507 ploc.memOffset = ploc.memOffset + static_cast<int32>(k4ByteSize);
7508 }
7509 SelectParmListPassByStack(*mirType, *opnd, static_cast<uint32>(ploc.memOffset), preCopyed, insnForStackArgs);
7510 }
7511 // if we have stack-passed arguments, don't do tailcall
7512 parmLocator.InitCCLocInfo(ploc);
7513 if (ploc.memOffset != 0) {
7514 naryNode.SetMayTailcall(false);
7515 }
7516 if (hasSpecialArg) {
7517 DEBUG_ASSERT(tmpBB, "need temp bb for lower priority args");
7518 SetCurBB(*tmpBB);
7519 for (auto it : regMapForTmpBB) {
7520 SelectCopy(*it.destReg, it.destType, *it.srcReg, it.srcType);
7521 }
7522 curBBrecord->InsertAtEnd(*tmpBB);
7523 SetCurBB(*curBBrecord);
7524 }
7525 for (auto &strInsn : insnForStackArgs) {
7526 GetCurBB()->AppendInsn(*strInsn);
7527 }
7528 }
7529
DoCallerEnsureValidParm(RegOperand & destOpnd,RegOperand & srcOpnd,PrimType formalPType)7530 bool AArch64CGFunc::DoCallerEnsureValidParm(RegOperand &destOpnd, RegOperand &srcOpnd, PrimType formalPType)
7531 {
7532 Insn *insn = nullptr;
7533 switch (formalPType) {
7534 case PTY_u1: {
7535 ImmOperand &lsbOpnd = CreateImmOperand(maplebe::k0BitSize, srcOpnd.GetSize(), false);
7536 ImmOperand &widthOpnd = CreateImmOperand(maplebe::k1BitSize, srcOpnd.GetSize(), false);
7537 bool is64Bit = (srcOpnd.GetSize() == maplebe::k64BitSize);
7538 insn = &GetInsnBuilder()->BuildInsn(is64Bit ? MOP_xubfxrri6i6 : MOP_wubfxrri5i5, destOpnd, srcOpnd, lsbOpnd,
7539 widthOpnd);
7540 break;
7541 }
7542 case PTY_u8:
7543 case PTY_i8:
7544 insn = &GetInsnBuilder()->BuildInsn(MOP_xuxtb32, destOpnd, srcOpnd);
7545 break;
7546 case PTY_u16:
7547 case PTY_i16:
7548 insn = &GetInsnBuilder()->BuildInsn(MOP_xuxth32, destOpnd, srcOpnd);
7549 break;
7550 default:
7551 break;
7552 }
7553 if (insn != nullptr) {
7554 GetCurBB()->AppendInsn(*insn);
7555 return true;
7556 }
7557 return false;
7558 }
7559
SelectParmListNotC(StmtNode & naryNode,ListOperand & srcOpnds)7560 void AArch64CGFunc::SelectParmListNotC(StmtNode &naryNode, ListOperand &srcOpnds)
7561 {
7562 size_t i = 0;
7563 if (naryNode.GetOpCode() == OP_icall || naryNode.GetOpCode() == OP_icallproto) {
7564 i++;
7565 }
7566
7567 CCImpl &parmLocator = *GetOrCreateLocator(CCImpl::GetCallConvKind(naryNode));
7568 CCLocInfo ploc;
7569 std::vector<Insn *> insnForStackArgs;
7570 uint32 stackArgsCount = 0;
7571 for (uint32 pnum = 0; i < naryNode.NumOpnds(); ++i, ++pnum) {
7572 MIRType *ty = nullptr;
7573 BaseNode *argExpr = naryNode.Opnd(i);
7574 DEBUG_ASSERT(argExpr != nullptr, "argExpr should not be nullptr");
7575 PrimType primType = argExpr->GetPrimType();
7576 DEBUG_ASSERT(primType != PTY_void, "primType should not be void");
7577 /* use alloca */
7578 ty = GlobalTables::GetTypeTable().GetTypeTable()[static_cast<uint32>(primType)];
7579 RegOperand *expRegOpnd = nullptr;
7580 Operand *opnd = HandleExpr(naryNode, *argExpr);
7581 if (!opnd->IsRegister()) {
7582 opnd = &LoadIntoRegister(*opnd, primType);
7583 }
7584 expRegOpnd = static_cast<RegOperand *>(opnd);
7585
7586 parmLocator.LocateNextParm(*ty, ploc);
7587 PrimType destPrimType = primType;
7588 if (ploc.reg0 != kRinvalid) { /* load to the register. */
7589 CHECK_FATAL(expRegOpnd != nullptr, "null ptr check");
7590 RegOperand &parmRegOpnd = GetOrCreatePhysicalRegisterOperand(
7591 static_cast<AArch64reg>(ploc.reg0), expRegOpnd->GetSize(), GetRegTyFromPrimTy(destPrimType));
7592 SelectCopy(parmRegOpnd, destPrimType, *expRegOpnd, primType);
7593 srcOpnds.PushOpnd(parmRegOpnd);
7594 } else { /* store to the memory segment for stack-passsed arguments. */
7595 if (CGOptions::IsBigEndian()) {
7596 if (GetPrimTypeBitSize(primType) < k64BitSize) {
7597 ploc.memOffset = ploc.memOffset + static_cast<int32>(k4BitSize);
7598 }
7599 }
7600 MemOperand &actMemOpnd = CreateMemOpnd(RSP, ploc.memOffset, GetPrimTypeBitSize(primType));
7601 Insn &strInsn = GetInsnBuilder()->BuildInsn(PickStInsn(GetPrimTypeBitSize(primType), primType), *expRegOpnd,
7602 actMemOpnd);
7603 actMemOpnd.SetStackArgMem(true);
7604 if (Globals::GetInstance()->GetOptimLevel() == CGOptions::kLevel1 && stackArgsCount < kShiftAmount12) {
7605 (void)insnForStackArgs.emplace_back(&strInsn);
7606 stackArgsCount++;
7607 } else {
7608 GetCurBB()->AppendInsn(strInsn);
7609 }
7610 }
7611 DEBUG_ASSERT(ploc.reg1 == 0, "SelectCall NYI");
7612 }
7613 for (auto &strInsn : insnForStackArgs) {
7614 GetCurBB()->AppendInsn(*strInsn);
7615 }
7616 }
7617
7618 // based on call conv, choose how to prepare args
SelectParmListWrapper(StmtNode & naryNode,ListOperand & srcOpnds,bool isCallNative)7619 void AArch64CGFunc::SelectParmListWrapper(StmtNode &naryNode, ListOperand &srcOpnds, bool isCallNative)
7620 {
7621 if (CCImpl::GetCallConvKind(naryNode) == kCCall) {
7622 SelectParmList(naryNode, srcOpnds, isCallNative);
7623 } else if (CCImpl::GetCallConvKind(naryNode) == kWebKitJS || CCImpl::GetCallConvKind(naryNode) == kGHC) {
7624 SelectParmListNotC(naryNode, srcOpnds);
7625 } else {
7626 CHECK_FATAL(false, "niy");
7627 }
7628 }
7629 /*
7630 * for MCC_DecRefResetPair(addrof ptr %Reg17_R5592, addrof ptr %Reg16_R6202) or
7631 * MCC_ClearLocalStackRef(addrof ptr %Reg17_R5592), the parameter (addrof ptr xxx) is converted to asm as follow:
7632 * add vreg, x29, #imm
7633 * mov R0/R1, vreg
7634 * this function is used to prepare parameters, the generated vreg is returned, and #imm is saved in offsetValue.
7635 */
SelectClearStackCallParam(const AddrofNode & expr,int64 & offsetValue)7636 Operand *AArch64CGFunc::SelectClearStackCallParam(const AddrofNode &expr, int64 &offsetValue)
7637 {
7638 CHECK_NULL_FATAL(mirModule.CurFunction());
7639 MIRSymbol *symbol = GetMirModule().CurFunction()->GetLocalOrGlobalSymbol(expr.GetStIdx());
7640 PrimType ptype = expr.GetPrimType();
7641 regno_t vRegNO = NewVReg(kRegTyInt, GetPrimTypeSize(ptype));
7642 Operand &result = CreateVirtualRegisterOperand(vRegNO);
7643 CHECK_FATAL(expr.GetFieldID() == 0, "the fieldID of parameter in clear stack reference call must be 0");
7644 if (!CGOptions::IsQuiet()) {
7645 maple::LogInfo::MapleLogger(kLlErr)
7646 << "Warning: we expect AddrOf with StImmOperand is not used for local variables";
7647 }
7648 auto *symLoc = static_cast<AArch64SymbolAlloc *>(GetMemlayout()->GetSymAllocInfo(symbol->GetStIndex()));
7649 ImmOperand *offset = nullptr;
7650 if (symLoc->GetMemSegment()->GetMemSegmentKind() == kMsArgsStkPassed) {
7651 offset = &CreateImmOperand(GetBaseOffset(*symLoc), k64BitSize, false, kUnAdjustVary);
7652 } else if (symLoc->GetMemSegment()->GetMemSegmentKind() == kMsRefLocals) {
7653 auto it = immOpndsRequiringOffsetAdjustmentForRefloc.find(symLoc);
7654 if (it != immOpndsRequiringOffsetAdjustmentForRefloc.end()) {
7655 offset = (*it).second;
7656 } else {
7657 offset = &CreateImmOperand(GetBaseOffset(*symLoc), k64BitSize, false);
7658 immOpndsRequiringOffsetAdjustmentForRefloc[symLoc] = offset;
7659 }
7660 } else {
7661 CHECK_FATAL(false, "the symLoc of parameter in clear stack reference call is unreasonable");
7662 }
7663 DEBUG_ASSERT(offset != nullptr, "offset should not be nullptr");
7664 offsetValue = offset->GetValue();
7665 SelectAdd(result, *GetBaseReg(*symLoc), *offset, PTY_u64);
7666 if (GetCG()->GenerateVerboseCG()) {
7667 /* Add a comment */
7668 Insn *insn = GetCurBB()->GetLastInsn();
7669 std::string comm = "local/formal var: ";
7670 comm += symbol->GetName();
7671 insn->SetComment(comm);
7672 }
7673 return &result;
7674 }
7675
7676 /* select paramters for MCC_DecRefResetPair and MCC_ClearLocalStackRef function */
SelectClearStackCallParmList(const StmtNode & naryNode,ListOperand & srcOpnds,std::vector<int64> & stackPostion)7677 void AArch64CGFunc::SelectClearStackCallParmList(const StmtNode &naryNode, ListOperand &srcOpnds,
7678 std::vector<int64> &stackPostion)
7679 {
7680 CHECK_FATAL(false, "should not go here");
7681 AArch64CallConvImpl parmLocator(GetBecommon());
7682 CCLocInfo ploc;
7683 for (size_t i = 0; i < naryNode.NumOpnds(); ++i) {
7684 MIRType *ty = nullptr;
7685 BaseNode *argExpr = naryNode.Opnd(i);
7686 PrimType primType = argExpr->GetPrimType();
7687 DEBUG_ASSERT(primType != PTY_void, "primType check");
7688 /* use alloc */
7689 CHECK_FATAL(primType != PTY_agg, "the type of argument is unreasonable");
7690 ty = GlobalTables::GetTypeTable().GetTypeTable()[static_cast<uint32>(primType)];
7691 CHECK_FATAL(argExpr->GetOpCode() == OP_addrof, "the argument of clear stack call is unreasonable");
7692 auto *expr = static_cast<AddrofNode *>(argExpr);
7693 int64 offsetValue = 0;
7694 Operand *opnd = SelectClearStackCallParam(*expr, offsetValue);
7695 stackPostion.emplace_back(offsetValue);
7696 auto *expRegOpnd = static_cast<RegOperand *>(opnd);
7697 parmLocator.LocateNextParm(*ty, ploc);
7698 CHECK_FATAL(ploc.reg0 != 0, "the parameter of ClearStackCall must be passed by register");
7699 CHECK_FATAL(expRegOpnd != nullptr, "null ptr check");
7700 RegOperand &parmRegOpnd = GetOrCreatePhysicalRegisterOperand(
7701 static_cast<AArch64reg>(ploc.reg0), expRegOpnd->GetSize(), GetRegTyFromPrimTy(primType));
7702 SelectCopy(parmRegOpnd, primType, *expRegOpnd, primType);
7703 srcOpnds.PushOpnd(parmRegOpnd);
7704 DEBUG_ASSERT(ploc.reg1 == 0, "SelectCall NYI");
7705 }
7706 }
7707
7708 /*
7709 * intrinsify Unsafe.getAndAddInt and Unsafe.getAndAddLong
7710 * generate an intrinsic instruction instead of a function call
7711 * intrinsic_get_add_int w0, xt, ws, ws, x1, x2, w3, label
7712 */
IntrinsifyGetAndAddInt(ListOperand & srcOpnds,PrimType pty)7713 void AArch64CGFunc::IntrinsifyGetAndAddInt(ListOperand &srcOpnds, PrimType pty)
7714 {
7715 MapleList<RegOperand *> &opnds = srcOpnds.GetOperands();
7716 /* Unsafe.getAndAddInt has more than 4 parameters */
7717 DEBUG_ASSERT(opnds.size() >= 4, "ensure the operands number");
7718 auto iter = opnds.begin();
7719 RegOperand *objOpnd = *(++iter);
7720 RegOperand *offOpnd = *(++iter);
7721 RegOperand *deltaOpnd = *(++iter);
7722 auto &retVal = static_cast<RegOperand &>(GetTargetRetOperand(pty, -1));
7723 LabelIdx labIdx = CreateLabel();
7724 LabelOperand &targetOpnd = GetOrCreateLabelOperand(labIdx);
7725 RegOperand &tempOpnd0 = CreateRegisterOperandOfType(PTY_i64);
7726 RegOperand &tempOpnd1 = CreateRegisterOperandOfType(pty);
7727 RegOperand &tempOpnd2 = CreateRegisterOperandOfType(PTY_i32);
7728 MOperator mOp = (pty == PTY_i64) ? MOP_get_and_addL : MOP_get_and_addI;
7729 std::vector<Operand *> intrnOpnds;
7730 intrnOpnds.emplace_back(&retVal);
7731 intrnOpnds.emplace_back(&tempOpnd0);
7732 intrnOpnds.emplace_back(&tempOpnd1);
7733 intrnOpnds.emplace_back(&tempOpnd2);
7734 intrnOpnds.emplace_back(objOpnd);
7735 intrnOpnds.emplace_back(offOpnd);
7736 intrnOpnds.emplace_back(deltaOpnd);
7737 intrnOpnds.emplace_back(&targetOpnd);
7738 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, intrnOpnds));
7739 }
7740
7741 /*
7742 * intrinsify Unsafe.getAndSetInt and Unsafe.getAndSetLong
7743 * generate an intrinsic instruction instead of a function call
7744 */
IntrinsifyGetAndSetInt(ListOperand & srcOpnds,PrimType pty)7745 void AArch64CGFunc::IntrinsifyGetAndSetInt(ListOperand &srcOpnds, PrimType pty)
7746 {
7747 MapleList<RegOperand *> &opnds = srcOpnds.GetOperands();
7748 /* Unsafe.getAndSetInt has 4 parameters */
7749 DEBUG_ASSERT(opnds.size() == 4, "ensure the operands number");
7750 auto iter = opnds.begin();
7751 RegOperand *objOpnd = *(++iter);
7752 RegOperand *offOpnd = *(++iter);
7753 RegOperand *newValueOpnd = *(++iter);
7754 auto &retVal = static_cast<RegOperand &>(GetTargetRetOperand(pty, -1));
7755 LabelIdx labIdx = CreateLabel();
7756 LabelOperand &targetOpnd = GetOrCreateLabelOperand(labIdx);
7757 RegOperand &tempOpnd0 = CreateRegisterOperandOfType(PTY_i64);
7758 RegOperand &tempOpnd1 = CreateRegisterOperandOfType(PTY_i32);
7759
7760 MOperator mOp = (pty == PTY_i64) ? MOP_get_and_setL : MOP_get_and_setI;
7761 std::vector<Operand *> intrnOpnds;
7762 intrnOpnds.emplace_back(&retVal);
7763 intrnOpnds.emplace_back(&tempOpnd0);
7764 intrnOpnds.emplace_back(&tempOpnd1);
7765 intrnOpnds.emplace_back(objOpnd);
7766 intrnOpnds.emplace_back(offOpnd);
7767 intrnOpnds.emplace_back(newValueOpnd);
7768 intrnOpnds.emplace_back(&targetOpnd);
7769 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, intrnOpnds));
7770 }
7771
7772 /*
7773 * intrinsify Unsafe.compareAndSwapInt and Unsafe.compareAndSwapLong
7774 * generate an intrinsic instruction instead of a function call
7775 */
IntrinsifyCompareAndSwapInt(ListOperand & srcOpnds,PrimType pty)7776 void AArch64CGFunc::IntrinsifyCompareAndSwapInt(ListOperand &srcOpnds, PrimType pty)
7777 {
7778 MapleList<RegOperand *> &opnds = srcOpnds.GetOperands();
7779 /* Unsafe.compareAndSwapInt has more than 5 parameters */
7780 DEBUG_ASSERT(opnds.size() >= 5, "ensure the operands number");
7781 auto iter = opnds.begin();
7782 RegOperand *objOpnd = *(++iter);
7783 RegOperand *offOpnd = *(++iter);
7784 RegOperand *expectedValueOpnd = *(++iter);
7785 RegOperand *newValueOpnd = *(++iter);
7786 auto &retVal = static_cast<RegOperand &>(GetTargetRetOperand(PTY_i64, -1));
7787 RegOperand &tempOpnd0 = CreateRegisterOperandOfType(PTY_i64);
7788 RegOperand &tempOpnd1 = CreateRegisterOperandOfType(pty);
7789 LabelIdx labIdx1 = CreateLabel();
7790 LabelOperand &label1Opnd = GetOrCreateLabelOperand(labIdx1);
7791 LabelIdx labIdx2 = CreateLabel();
7792 LabelOperand &label2Opnd = GetOrCreateLabelOperand(labIdx2);
7793 MOperator mOp = (pty == PTY_i32) ? MOP_compare_and_swapI : MOP_compare_and_swapL;
7794 std::vector<Operand *> intrnOpnds;
7795 intrnOpnds.emplace_back(&retVal);
7796 intrnOpnds.emplace_back(&tempOpnd0);
7797 intrnOpnds.emplace_back(&tempOpnd1);
7798 intrnOpnds.emplace_back(objOpnd);
7799 intrnOpnds.emplace_back(offOpnd);
7800 intrnOpnds.emplace_back(expectedValueOpnd);
7801 intrnOpnds.emplace_back(newValueOpnd);
7802 intrnOpnds.emplace_back(&label1Opnd);
7803 intrnOpnds.emplace_back(&label2Opnd);
7804 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, intrnOpnds));
7805 }
7806
7807 /*
7808 * the lowest bit of count field is used to indicate whether or not the string is compressed
7809 * if the string is not compressed, jump to jumpLabIdx
7810 */
CheckStringIsCompressed(BB & bb,RegOperand & str,int32 countOffset,PrimType countPty,LabelIdx jumpLabIdx)7811 RegOperand *AArch64CGFunc::CheckStringIsCompressed(BB &bb, RegOperand &str, int32 countOffset, PrimType countPty,
7812 LabelIdx jumpLabIdx)
7813 {
7814 MemOperand &memOpnd = CreateMemOpnd(str, countOffset, str.GetSize());
7815 uint32 bitSize = GetPrimTypeBitSize(countPty);
7816 MOperator loadOp = PickLdInsn(bitSize, countPty);
7817 RegOperand &countOpnd = CreateRegisterOperandOfType(countPty);
7818 bb.AppendInsn(GetInsnBuilder()->BuildInsn(loadOp, countOpnd, memOpnd));
7819 ImmOperand &immValueOne = CreateImmOperand(countPty, 1);
7820 RegOperand &countLowestBitOpnd = CreateRegisterOperandOfType(countPty);
7821 MOperator andOp = bitSize == k64BitSize ? MOP_xandrri13 : MOP_wandrri12;
7822 bb.AppendInsn(GetInsnBuilder()->BuildInsn(andOp, countLowestBitOpnd, countOpnd, immValueOne));
7823 RegOperand &wzr = GetZeroOpnd(bitSize);
7824 MOperator cmpOp = (bitSize == k64BitSize) ? MOP_xcmprr : MOP_wcmprr;
7825 Operand &rflag = GetOrCreateRflag();
7826 bb.AppendInsn(GetInsnBuilder()->BuildInsn(cmpOp, rflag, wzr, countLowestBitOpnd));
7827 bb.AppendInsn(GetInsnBuilder()->BuildInsn(MOP_beq, rflag, GetOrCreateLabelOperand(jumpLabIdx)));
7828 bb.SetKind(BB::kBBIf);
7829 return &countOpnd;
7830 }
7831
7832 /*
7833 * count field stores the length shifted one bit to the left
7834 * if the length is less than eight, jump to jumpLabIdx
7835 */
CheckStringLengthLessThanEight(BB & bb,RegOperand & countOpnd,PrimType countPty,LabelIdx jumpLabIdx)7836 RegOperand *AArch64CGFunc::CheckStringLengthLessThanEight(BB &bb, RegOperand &countOpnd, PrimType countPty,
7837 LabelIdx jumpLabIdx)
7838 {
7839 RegOperand &lengthOpnd = CreateRegisterOperandOfType(countPty);
7840 uint32 bitSize = GetPrimTypeBitSize(countPty);
7841 MOperator lsrOp = (bitSize == k64BitSize) ? MOP_xlsrrri6 : MOP_wlsrrri5;
7842 ImmOperand &immValueOne = CreateImmOperand(countPty, 1);
7843 bb.AppendInsn(GetInsnBuilder()->BuildInsn(lsrOp, lengthOpnd, countOpnd, immValueOne));
7844 constexpr int kConstIntEight = 8;
7845 ImmOperand &immValueEight = CreateImmOperand(countPty, kConstIntEight);
7846 MOperator cmpImmOp = (bitSize == k64BitSize) ? MOP_xcmpri : MOP_wcmpri;
7847 Operand &rflag = GetOrCreateRflag();
7848 bb.AppendInsn(GetInsnBuilder()->BuildInsn(cmpImmOp, rflag, lengthOpnd, immValueEight));
7849 bb.AppendInsn(GetInsnBuilder()->BuildInsn(MOP_blt, rflag, GetOrCreateLabelOperand(jumpLabIdx)));
7850 bb.SetKind(BB::kBBIf);
7851 return &lengthOpnd;
7852 }
7853
GenerateIntrnInsnForStrIndexOf(BB & bb,RegOperand & srcString,RegOperand & patternString,RegOperand & srcCountOpnd,RegOperand & patternLengthOpnd,PrimType countPty,LabelIdx jumpLabIdx)7854 void AArch64CGFunc::GenerateIntrnInsnForStrIndexOf(BB &bb, RegOperand &srcString, RegOperand &patternString,
7855 RegOperand &srcCountOpnd, RegOperand &patternLengthOpnd,
7856 PrimType countPty, LabelIdx jumpLabIdx)
7857 {
7858 RegOperand &srcLengthOpnd = CreateRegisterOperandOfType(countPty);
7859 ImmOperand &immValueOne = CreateImmOperand(countPty, 1);
7860 uint32 bitSize = GetPrimTypeBitSize(countPty);
7861 MOperator lsrOp = (bitSize == k64BitSize) ? MOP_xlsrrri6 : MOP_wlsrrri5;
7862 bb.AppendInsn(GetInsnBuilder()->BuildInsn(lsrOp, srcLengthOpnd, srcCountOpnd, immValueOne));
7863 #ifdef USE_32BIT_REF
7864 const int64 stringBaseObjSize = 16; /* shadow(4)+monitor(4)+count(4)+hash(4) */
7865 #else
7866 const int64 stringBaseObjSize = 20; /* shadow(8)+monitor(4)+count(4)+hash(4) */
7867 #endif /* USE_32BIT_REF */
7868 PrimType pty = (srcString.GetSize() == k64BitSize) ? PTY_i64 : PTY_i32;
7869 ImmOperand &immStringBaseOffset = CreateImmOperand(pty, stringBaseObjSize);
7870 MOperator addOp = (pty == PTY_i64) ? MOP_xaddrri12 : MOP_waddrri12;
7871 RegOperand &srcStringBaseOpnd = CreateRegisterOperandOfType(pty);
7872 bb.AppendInsn(GetInsnBuilder()->BuildInsn(addOp, srcStringBaseOpnd, srcString, immStringBaseOffset));
7873 RegOperand &patternStringBaseOpnd = CreateRegisterOperandOfType(pty);
7874 bb.AppendInsn(GetInsnBuilder()->BuildInsn(addOp, patternStringBaseOpnd, patternString, immStringBaseOffset));
7875 auto &retVal = static_cast<RegOperand &>(GetTargetRetOperand(PTY_i32, -1));
7876 std::vector<Operand *> intrnOpnds;
7877 intrnOpnds.emplace_back(&retVal);
7878 intrnOpnds.emplace_back(&srcStringBaseOpnd);
7879 intrnOpnds.emplace_back(&srcLengthOpnd);
7880 intrnOpnds.emplace_back(&patternStringBaseOpnd);
7881 intrnOpnds.emplace_back(&patternLengthOpnd);
7882 const uint32 tmpRegOperandNum = 6;
7883 for (uint32 i = 0; i < tmpRegOperandNum - 1; ++i) {
7884 RegOperand &tmpOpnd = CreateRegisterOperandOfType(PTY_i64);
7885 intrnOpnds.emplace_back(&tmpOpnd);
7886 }
7887 intrnOpnds.emplace_back(&CreateRegisterOperandOfType(PTY_i32));
7888 const uint32 labelNum = 7;
7889 for (uint32 i = 0; i < labelNum; ++i) {
7890 LabelIdx labIdx = CreateLabel();
7891 LabelOperand &labelOpnd = GetOrCreateLabelOperand(labIdx);
7892 intrnOpnds.emplace_back(&labelOpnd);
7893 }
7894 bb.AppendInsn(GetInsnBuilder()->BuildInsn(MOP_string_indexof, intrnOpnds));
7895 bb.AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xuncond, GetOrCreateLabelOperand(jumpLabIdx)));
7896 bb.SetKind(BB::kBBGoto);
7897 }
7898
SelectCall(CallNode & callNode)7899 void AArch64CGFunc::SelectCall(CallNode &callNode)
7900 {
7901 MIRFunction *fn = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(callNode.GetPUIdx());
7902 MIRSymbol *fsym = GetFunction().GetLocalOrGlobalSymbol(fn->GetStIdx(), false);
7903 MIRType *retType = fn->GetReturnType();
7904
7905 if (GetCG()->GenerateVerboseCG()) {
7906 const std::string &comment = fsym->GetName();
7907 GetCurBB()->AppendInsn(CreateCommentInsn(comment));
7908 }
7909
7910 ListOperand *srcOpnds = CreateListOpnd(*GetFuncScopeAllocator());
7911 if (GetMirModule().GetFlavor() == MIRFlavor::kFlavorLmbc) {
7912 SetLmbcCallReturnType(nullptr);
7913 if (fn->IsFirstArgReturn()) {
7914 MIRPtrType *ptrTy = static_cast<MIRPtrType *>(
7915 GlobalTables::GetTypeTable().GetTypeFromTyIdx(fn->GetFormalDefVec()[0].formalTyIdx));
7916 MIRType *sTy = GlobalTables::GetTypeTable().GetTypeFromTyIdx(ptrTy->GetPointedTyIdx());
7917 SetLmbcCallReturnType(sTy);
7918 } else {
7919 MIRType *ty = fn->GetReturnType();
7920 SetLmbcCallReturnType(ty);
7921 }
7922 }
7923
7924 SelectParmListWrapper(callNode, *srcOpnds, false);
7925
7926 Insn &callInsn = AppendCall(*fsym, *srcOpnds);
7927 GetCurBB()->SetHasCall();
7928 if (retType != nullptr) {
7929 callInsn.SetRetSize(static_cast<uint32>(retType->GetSize()));
7930 callInsn.SetIsCallReturnUnsigned(IsUnsignedInteger(retType->GetPrimType()));
7931 }
7932 const auto &deoptBundleInfo = callNode.GetDeoptBundleInfo();
7933 for (const auto &elem : deoptBundleInfo) {
7934 auto valueKind = elem.second.GetMapleValueKind();
7935 if (valueKind == MapleValue::kPregKind) {
7936 auto *opnd = GetOpndFromPregIdx(elem.second.GetPregIdx());
7937 CHECK_FATAL(opnd != nullptr, "pregIdx has not been assigned Operand");
7938 callInsn.AddDeoptBundleInfo(elem.first, *opnd);
7939 } else if (valueKind == MapleValue::kConstKind) {
7940 auto *opnd = SelectIntConst(static_cast<const MIRIntConst &>(elem.second.GetConstValue()), callNode);
7941 callInsn.AddDeoptBundleInfo(elem.first, *opnd);
7942 } else {
7943 CHECK_FATAL(false, "not supported currently");
7944 }
7945 }
7946 AppendStackMapInsn(callInsn);
7947
7948 /* check if this call use stack slot to return */
7949 if (fn->IsFirstArgReturn()) {
7950 SetStackProtectInfo(kRetureStackSlot);
7951 }
7952
7953 GetFunction().SetHasCall();
7954 if (GetMirModule().IsCModule()) { /* do not mark abort BB in C at present */
7955 if (fsym->GetName() == "__builtin_unreachable") {
7956 GetCurBB()->ClearInsns();
7957 GetCurBB()->SetUnreachable(true);
7958 }
7959 return;
7960 }
7961 }
7962
SelectIcall(IcallNode & icallNode)7963 void AArch64CGFunc::SelectIcall(IcallNode &icallNode)
7964 {
7965 ListOperand *srcOpnds = CreateListOpnd(*GetFuncScopeAllocator());
7966 SelectParmListWrapper(icallNode, *srcOpnds, false);
7967
7968 Operand *srcOpnd = HandleExpr(icallNode, *icallNode.GetNopndAt(0));
7969 Operand *fptrOpnd = srcOpnd;
7970 if (fptrOpnd->GetKind() != Operand::kOpdRegister) {
7971 PrimType ty = icallNode.Opnd(0)->GetPrimType();
7972 fptrOpnd = &SelectCopy(*srcOpnd, ty, ty);
7973 }
7974 DEBUG_ASSERT(fptrOpnd->IsRegister(), "SelectIcall: function pointer not RegOperand");
7975 RegOperand *regOpnd = static_cast<RegOperand *>(fptrOpnd);
7976 Insn &callInsn = GetInsnBuilder()->BuildInsn(MOP_xblr, *regOpnd, *srcOpnds);
7977
7978 MIRType *retType = icallNode.GetCallReturnType();
7979 if (retType != nullptr) {
7980 callInsn.SetRetSize(static_cast<uint32>(retType->GetSize()));
7981 callInsn.SetIsCallReturnUnsigned(IsUnsignedInteger(retType->GetPrimType()));
7982 }
7983
7984 /* check if this icall use stack slot to return */
7985 CallReturnVector *p2nrets = &icallNode.GetReturnVec();
7986 if (p2nrets->size() == k1ByteSize) {
7987 StIdx stIdx = (*p2nrets)[0].first;
7988 CHECK_NULL_FATAL(mirModule.CurFunction());
7989 MIRSymbol *sym = GetBecommon().GetMIRModule().CurFunction()->GetSymTab()->GetSymbolFromStIdx(stIdx.Idx());
7990 if (sym != nullptr && (GetBecommon().GetTypeSize(sym->GetTyIdx().GetIdx()) > k16ByteSize)) {
7991 SetStackProtectInfo(kRetureStackSlot);
7992 }
7993 }
7994
7995 GetCurBB()->AppendInsn(callInsn);
7996 GetCurBB()->SetHasCall();
7997 DEBUG_ASSERT(GetCurBB()->GetLastMachineInsn()->IsCall(), "lastInsn should be a call");
7998 GetFunction().SetHasCall();
7999 const auto &deoptBundleInfo = icallNode.GetDeoptBundleInfo();
8000 for (const auto &elem : deoptBundleInfo) {
8001 auto valueKind = elem.second.GetMapleValueKind();
8002 if (valueKind == MapleValue::kPregKind) {
8003 auto *opnd = GetOpndFromPregIdx(elem.second.GetPregIdx());
8004 CHECK_FATAL(opnd != nullptr, "pregIdx has not been assigned Operand");
8005 callInsn.AddDeoptBundleInfo(elem.first, *opnd);
8006 } else if (valueKind == MapleValue::kConstKind) {
8007 auto *opnd = SelectIntConst(static_cast<const MIRIntConst &>(elem.second.GetConstValue()), icallNode);
8008 callInsn.AddDeoptBundleInfo(elem.first, *opnd);
8009 } else {
8010 CHECK_FATAL(false, "not supported currently");
8011 }
8012 }
8013 AppendStackMapInsn(callInsn);
8014 }
8015
HandleCatch()8016 void AArch64CGFunc::HandleCatch()
8017 {
8018 if (Globals::GetInstance()->GetOptimLevel() >= CGOptions::kLevel1) {
8019 regno_t regNO = uCatch.regNOCatch;
8020 RegOperand &vregOpnd = GetOrCreateVirtualRegisterOperand(regNO);
8021 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(
8022 MOP_xmovrr, vregOpnd, GetOrCreatePhysicalRegisterOperand(R0, k64BitSize, kRegTyInt)));
8023 } else {
8024 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(
8025 PickStInsn(uCatch.opndCatch->GetSize(), PTY_a64),
8026 GetOrCreatePhysicalRegisterOperand(R0, k64BitSize, kRegTyInt), *uCatch.opndCatch));
8027 }
8028 }
8029
SelectMembar(StmtNode & membar)8030 void AArch64CGFunc::SelectMembar(StmtNode &membar)
8031 {
8032 switch (membar.GetOpCode()) {
8033 case OP_membaracquire:
8034 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_dmb_ishld, AArch64CG::kMd[MOP_dmb_ishld]));
8035 break;
8036 case OP_membarrelease:
8037 case OP_membarstoreload:
8038 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_dmb_ish, AArch64CG::kMd[MOP_dmb_ish]));
8039 break;
8040 case OP_membarstorestore:
8041 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_dmb_ishst, AArch64CG::kMd[MOP_dmb_ishst]));
8042 break;
8043 default:
8044 DEBUG_ASSERT(false, "NYI");
8045 break;
8046 }
8047 }
8048
SelectComment(CommentNode & comment)8049 void AArch64CGFunc::SelectComment(CommentNode &comment)
8050 {
8051 GetCurBB()->AppendInsn(CreateCommentInsn(comment.GetComment()));
8052 }
8053
SelectReturn(Operand * opnd0)8054 void AArch64CGFunc::SelectReturn(Operand *opnd0)
8055 {
8056 bool is64x1vec = GetFunction().GetAttr(FUNCATTR_oneelem_simd) ? true : false;
8057 MIRType *floatType = GlobalTables::GetTypeTable().GetDouble();
8058 MIRType *retTyp = is64x1vec ? floatType : GetFunction().GetReturnType();
8059 CCImpl &retLocator = *GetOrCreateLocator(GetCurCallConvKind());
8060 CCLocInfo retMech;
8061 retLocator.LocateRetVal(*retTyp, retMech);
8062 if ((retMech.GetRegCount() > 0) && (opnd0 != nullptr)) {
8063 RegType regTyp = is64x1vec ? kRegTyFloat : GetRegTyFromPrimTy(retMech.GetPrimTypeOfReg0());
8064 PrimType oriPrimType = is64x1vec ? GetFunction().GetReturnType()->GetPrimType() : retMech.GetPrimTypeOfReg0();
8065 AArch64reg retReg = static_cast<AArch64reg>(retMech.GetReg0());
8066 if (opnd0->IsRegister()) {
8067 RegOperand *regOpnd = static_cast<RegOperand *>(opnd0);
8068 if (regOpnd->GetRegisterNumber() != retMech.GetReg0()) {
8069 RegOperand &retOpnd = GetOrCreatePhysicalRegisterOperand(retReg, regOpnd->GetSize(), regTyp);
8070 SelectCopy(retOpnd, retMech.GetPrimTypeOfReg0(), *regOpnd, oriPrimType);
8071 }
8072 } else if (opnd0->IsMemoryAccessOperand()) {
8073 auto *memopnd = static_cast<MemOperand *>(opnd0);
8074 RegOperand &retOpnd =
8075 GetOrCreatePhysicalRegisterOperand(retReg, GetPrimTypeBitSize(retMech.GetPrimTypeOfReg0()), regTyp);
8076 MOperator mOp = PickLdInsn(memopnd->GetSize(), retMech.GetPrimTypeOfReg0());
8077 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, retOpnd, *memopnd));
8078 } else if (opnd0->IsConstImmediate()) {
8079 ImmOperand *immOpnd = static_cast<ImmOperand *>(opnd0);
8080 if (!is64x1vec) {
8081 RegOperand &retOpnd =
8082 GetOrCreatePhysicalRegisterOperand(retReg, GetPrimTypeBitSize(retMech.GetPrimTypeOfReg0()),
8083 GetRegTyFromPrimTy(retMech.GetPrimTypeOfReg0()));
8084 SelectCopy(retOpnd, retMech.GetPrimTypeOfReg0(), *immOpnd, retMech.GetPrimTypeOfReg0());
8085 } else {
8086 PrimType rType = GetFunction().GetReturnType()->GetPrimType();
8087 RegOperand *reg = &CreateRegisterOperandOfType(rType);
8088 SelectCopy(*reg, rType, *immOpnd, rType);
8089 RegOperand &retOpnd = GetOrCreatePhysicalRegisterOperand(retReg, GetPrimTypeBitSize(PTY_f64),
8090 GetRegTyFromPrimTy(PTY_f64));
8091 Insn &insn = GetInsnBuilder()->BuildInsn(MOP_xvmovdr, retOpnd, *reg);
8092 GetCurBB()->AppendInsn(insn);
8093 }
8094 } else {
8095 CHECK_FATAL(false, "nyi");
8096 }
8097 }
8098 GetExitBBsVec().emplace_back(GetCurBB());
8099 }
8100
GetOrCreateSpecialRegisterOperand(PregIdx sregIdx,PrimType primType)8101 RegOperand &AArch64CGFunc::GetOrCreateSpecialRegisterOperand(PregIdx sregIdx, PrimType primType)
8102 {
8103 switch (sregIdx) {
8104 case kSregSp:
8105 return GetOrCreatePhysicalRegisterOperand(RSP, k64BitSize, kRegTyInt);
8106 case kSregFp:
8107 return GetOrCreatePhysicalRegisterOperand(RFP, k64BitSize, kRegTyInt);
8108 case kSregGp: {
8109 MIRSymbol *sym = GetCG()->GetGP();
8110 if (sym == nullptr) {
8111 sym = GetFunction().GetSymTab()->CreateSymbol(kScopeLocal);
8112 std::string strBuf("__file__local__GP");
8113 sym->SetNameStrIdx(GetMirModule().GetMIRBuilder()->GetOrCreateStringIndex(strBuf));
8114 GetCG()->SetGP(sym);
8115 }
8116 RegOperand &result = GetOrCreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
8117 SelectAddrof(result, CreateStImmOperand(*sym, 0, 0));
8118 return result;
8119 }
8120 case kSregThrownval: { /* uses x0 == R0 */
8121 DEBUG_ASSERT(uCatch.regNOCatch > 0, "regNOCatch should greater than 0.");
8122 if (Globals::GetInstance()->GetOptimLevel() == CGOptions::kLevel0) {
8123 RegOperand ®Opnd = GetOrCreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8BitSize));
8124 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(PickLdInsn(uCatch.opndCatch->GetSize(), PTY_a64),
8125 regOpnd, *uCatch.opndCatch));
8126 return regOpnd;
8127 } else {
8128 return GetOrCreateVirtualRegisterOperand(uCatch.regNOCatch);
8129 }
8130 }
8131 case kSregMethodhdl:
8132 if (methodHandleVreg == regno_t(-1)) {
8133 methodHandleVreg = NewVReg(kRegTyInt, k8BitSize);
8134 }
8135 return GetOrCreateVirtualRegisterOperand(methodHandleVreg);
8136 default:
8137 break;
8138 }
8139
8140 // process the 128-bit return value
8141 if (IsInt128Ty(primType)) {
8142 Operand &low = GetOrCreatePhysicalRegisterOperand(R0, k64BitSize, GetRegTyFromPrimTy(PTY_u64));
8143 Operand &high = GetOrCreatePhysicalRegisterOperand(R1, k64BitSize, GetRegTyFromPrimTy(PTY_u64));
8144 return CombineInt128({low, high});
8145 }
8146
8147 bool useFpReg = !IsPrimitiveInteger(primType) || IsPrimitiveVectorFloat(primType);
8148 AArch64reg pReg = RLAST_INT_REG;
8149 switch (sregIdx) {
8150 case kSregRetval0:
8151 pReg = useFpReg ? V0 : R0;
8152 break;
8153 case kSregRetval1:
8154 pReg = useFpReg ? V1 : R1;
8155 break;
8156 case kSregRetval2:
8157 pReg = V2;
8158 break;
8159 case kSregRetval3:
8160 pReg = V3;
8161 break;
8162 default:
8163 DEBUG_ASSERT(false, "Special pseudo registers NYI");
8164 break;
8165 }
8166 uint32 bitSize = GetPrimTypeBitSize(primType);
8167 bitSize = bitSize <= k32BitSize ? k32BitSize : bitSize;
8168 auto &phyOpnd = GetOrCreatePhysicalRegisterOperand(pReg, bitSize, GetRegTyFromPrimTy(primType));
8169 return SelectCopy(phyOpnd, primType, primType); // most opt only deal vreg, so return a vreg
8170 }
8171
GetOrCreatePhysicalRegisterOperand(std::string & asmAttr)8172 RegOperand &AArch64CGFunc::GetOrCreatePhysicalRegisterOperand(std::string &asmAttr)
8173 {
8174 DEBUG_ASSERT(!asmAttr.empty(), "Get inline asm string failed in GetOrCreatePhysicalRegisterOperand");
8175 RegType rKind = kRegTyUndef;
8176 uint32 rSize = 0;
8177 /* Get Register Type and Size */
8178 switch (asmAttr[0]) {
8179 case 'x': {
8180 rKind = kRegTyInt;
8181 rSize = k64BitSize;
8182 break;
8183 }
8184 case 'w': {
8185 rKind = kRegTyInt;
8186 rSize = k32BitSize;
8187 break;
8188 }
8189 default: {
8190 LogInfo::MapleLogger() << "Unsupport asm string : " << asmAttr << "\n";
8191 CHECK_FATAL(false, "Have not support this kind of register ");
8192 }
8193 }
8194 AArch64reg rNO = kRinvalid;
8195 /* Get Register Number */
8196 uint32 regNumPos = 1;
8197 char numberChar = asmAttr[regNumPos++];
8198 if (numberChar >= '0' && numberChar <= '9') {
8199 uint32 val = static_cast<uint32>(numberChar - '0');
8200 if (regNumPos < asmAttr.length()) {
8201 char numberCharSecond = asmAttr[regNumPos++];
8202 DEBUG_ASSERT(regNumPos == asmAttr.length(), "Invalid asm attribute");
8203 if (numberCharSecond >= '0' && numberCharSecond <= '9') {
8204 val = val * kDecimalMax + static_cast<uint32>((numberCharSecond - '0'));
8205 }
8206 }
8207 rNO = static_cast<AArch64reg>(static_cast<uint32>(R0) + val);
8208 if (val > (kAsmInputRegPrefixOpnd + 1)) {
8209 LogInfo::MapleLogger() << "Unsupport asm string : " << asmAttr << "\n";
8210 CHECK_FATAL(false, "have not support this kind of register ");
8211 }
8212 } else if (numberChar == 0) {
8213 return CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
8214 } else {
8215 CHECK_FATAL(false, "Unexpect input in GetOrCreatePhysicalRegisterOperand");
8216 }
8217 return GetOrCreatePhysicalRegisterOperand(rNO, rSize, rKind);
8218 }
8219
GetOrCreatePhysicalRegisterOperand(AArch64reg regNO,uint32 size,RegType kind,uint32 flag)8220 RegOperand &AArch64CGFunc::GetOrCreatePhysicalRegisterOperand(AArch64reg regNO, uint32 size, RegType kind, uint32 flag)
8221 {
8222 uint64 aarch64PhyRegIdx = regNO;
8223 DEBUG_ASSERT(flag == 0, "Do not expect flag here");
8224 if (size <= k32BitSize) {
8225 size = k32BitSize;
8226 aarch64PhyRegIdx = aarch64PhyRegIdx << 1;
8227 } else if (size <= k64BitSize) {
8228 size = k64BitSize;
8229 aarch64PhyRegIdx = (aarch64PhyRegIdx << 1) + 1;
8230 } else {
8231 size = (size == k128BitSize) ? k128BitSize : k64BitSize;
8232 aarch64PhyRegIdx = aarch64PhyRegIdx << k4BitShift;
8233 }
8234 RegOperand *phyRegOpnd = nullptr;
8235 auto phyRegIt = phyRegOperandTable.find(aarch64PhyRegIdx);
8236 if (phyRegIt != phyRegOperandTable.end()) {
8237 phyRegOpnd = phyRegOperandTable[aarch64PhyRegIdx];
8238 } else {
8239 phyRegOpnd = memPool->New<RegOperand>(regNO, size, kind, flag);
8240 phyRegOperandTable.emplace(aarch64PhyRegIdx, phyRegOpnd);
8241 }
8242 return *phyRegOpnd;
8243 }
8244
GetLabelOperand(LabelIdx labIdx) const8245 const LabelOperand *AArch64CGFunc::GetLabelOperand(LabelIdx labIdx) const
8246 {
8247 const MapleUnorderedMap<LabelIdx, LabelOperand *>::const_iterator it = hashLabelOpndTable.find(labIdx);
8248 if (it != hashLabelOpndTable.end()) {
8249 return it->second;
8250 }
8251 return nullptr;
8252 }
8253
GetOrCreateLabelOperand(LabelIdx labIdx)8254 LabelOperand &AArch64CGFunc::GetOrCreateLabelOperand(LabelIdx labIdx)
8255 {
8256 MapleUnorderedMap<LabelIdx, LabelOperand *>::iterator it = hashLabelOpndTable.find(labIdx);
8257 if (it != hashLabelOpndTable.end()) {
8258 return *(it->second);
8259 }
8260 LabelOperand *res = memPool->New<LabelOperand>(GetShortFuncName().c_str(), labIdx, *memPool);
8261 hashLabelOpndTable[labIdx] = res;
8262 return *res;
8263 }
8264
GetOrCreateLabelOperand(BB & bb)8265 LabelOperand &AArch64CGFunc::GetOrCreateLabelOperand(BB &bb)
8266 {
8267 LabelIdx labelIdx = bb.GetLabIdx();
8268 if (labelIdx == MIRLabelTable::GetDummyLabel()) {
8269 labelIdx = CreateLabel();
8270 bb.AddLabel(labelIdx);
8271 SetLab2BBMap(labelIdx, bb);
8272 }
8273 return GetOrCreateLabelOperand(labelIdx);
8274 }
8275
GetAggCopySize(uint32 offset1,uint32 offset2,uint32 alignment) const8276 uint32 AArch64CGFunc::GetAggCopySize(uint32 offset1, uint32 offset2, uint32 alignment) const
8277 {
8278 /* Generating a larger sized mem op than alignment if allowed by aggregate starting address */
8279 uint32 offsetAlign1 = (offset1 == 0) ? k8ByteSize : offset1;
8280 uint32 offsetAlign2 = (offset2 == 0) ? k8ByteSize : offset2;
8281 uint32 alignOffset =
8282 1U << (std::min(__builtin_ffs(static_cast<int>(offsetAlign1)), __builtin_ffs(static_cast<int>(offsetAlign2))) -
8283 1);
8284 if (alignOffset == k8ByteSize || alignOffset == k4ByteSize || alignOffset == k2ByteSize) {
8285 return alignOffset;
8286 } else if (alignOffset > k8ByteSize) {
8287 return k8ByteSize;
8288 } else {
8289 return alignment;
8290 }
8291 }
8292
GetOrCreateOfstOpnd(uint64 offset,uint32 size)8293 OfstOperand &AArch64CGFunc::GetOrCreateOfstOpnd(uint64 offset, uint32 size)
8294 {
8295 uint64 aarch64OfstRegIdx = offset;
8296 aarch64OfstRegIdx = (aarch64OfstRegIdx << 1);
8297 if (size == k64BitSize) {
8298 ++aarch64OfstRegIdx;
8299 }
8300 DEBUG_ASSERT(size == k32BitSize || size == k64BitSize, "ofStOpnd size check");
8301 auto it = hashOfstOpndTable.find(aarch64OfstRegIdx);
8302 if (it != hashOfstOpndTable.end()) {
8303 return *it->second;
8304 }
8305 OfstOperand *res = &CreateOfstOpnd(offset, size);
8306 hashOfstOpndTable[aarch64OfstRegIdx] = res;
8307 return *res;
8308 }
8309
SelectAddrofAfterRa(Operand & result,StImmOperand & stImm,std::vector<Insn * > & rematInsns)8310 void AArch64CGFunc::SelectAddrofAfterRa(Operand &result, StImmOperand &stImm, std::vector<Insn *> &rematInsns)
8311 {
8312 const MIRSymbol *symbol = stImm.GetSymbol();
8313 DEBUG_ASSERT((symbol->GetStorageClass() != kScAuto) || (symbol->GetStorageClass() != kScFormal), "");
8314 Operand *srcOpnd = &result;
8315 rematInsns.emplace_back(&GetInsnBuilder()->BuildInsn(MOP_xadrp, result, stImm));
8316 if (CGOptions::IsPIC() && symbol->NeedPIC()) {
8317 OfstOperand &offset = CreateOfstOpnd(*stImm.GetSymbol(), stImm.GetOffset(), stImm.GetRelocs());
8318 MemOperand &memOpnd = GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, GetPointerSize() * kBitsPerByte,
8319 static_cast<RegOperand *>(srcOpnd), nullptr, &offset, nullptr);
8320 rematInsns.emplace_back(
8321 &GetInsnBuilder()->BuildInsn(memOpnd.GetSize() == k64BitSize ? MOP_xldr : MOP_wldr, result, memOpnd));
8322
8323 if (stImm.GetOffset() > 0) {
8324 ImmOperand &immOpnd = CreateImmOperand(stImm.GetOffset(), result.GetSize(), false);
8325 rematInsns.emplace_back(&GetInsnBuilder()->BuildInsn(MOP_xaddrri12, result, result, immOpnd));
8326 return;
8327 }
8328 } else {
8329 rematInsns.emplace_back(&GetInsnBuilder()->BuildInsn(MOP_xadrpl12, result, *srcOpnd, stImm));
8330 }
8331 }
8332
GetOrCreateMemOpndAfterRa(const MIRSymbol & symbol,int32 offset,uint32 size,bool needLow12,RegOperand * regOp,std::vector<Insn * > & rematInsns)8333 MemOperand &AArch64CGFunc::GetOrCreateMemOpndAfterRa(const MIRSymbol &symbol, int32 offset, uint32 size, bool needLow12,
8334 RegOperand *regOp, std::vector<Insn *> &rematInsns)
8335 {
8336 MIRStorageClass storageClass = symbol.GetStorageClass();
8337 if ((storageClass == kScGlobal) || (storageClass == kScExtern)) {
8338 StImmOperand &stOpnd = CreateStImmOperand(symbol, offset, 0);
8339 RegOperand &stAddrOpnd = *regOp;
8340 SelectAddrofAfterRa(stAddrOpnd, stOpnd, rematInsns);
8341 /* MemOperand::AddrMode_B_OI */
8342 return *CreateMemOperand(MemOperand::kAddrModeBOi, size, stAddrOpnd, nullptr,
8343 &GetOrCreateOfstOpnd(0, k32BitSize), &symbol);
8344 } else if ((storageClass == kScPstatic) || (storageClass == kScFstatic)) {
8345 if (symbol.GetSKind() == kStConst) {
8346 DEBUG_ASSERT(offset == 0, "offset should be 0 for constant literals");
8347 return *CreateMemOperand(MemOperand::kAddrModeLiteral, size, symbol);
8348 } else {
8349 if (needLow12) {
8350 StImmOperand &stOpnd = CreateStImmOperand(symbol, offset, 0);
8351 RegOperand &stAddrOpnd = *regOp;
8352 SelectAddrofAfterRa(stAddrOpnd, stOpnd, rematInsns);
8353 return *CreateMemOperand(MemOperand::kAddrModeBOi, size, stAddrOpnd, nullptr,
8354 &GetOrCreateOfstOpnd(0, k32BitSize), &symbol);
8355 } else {
8356 StImmOperand &stOpnd = CreateStImmOperand(symbol, offset, 0);
8357 RegOperand &stAddrOpnd = *regOp;
8358 Insn &insn = GetInsnBuilder()->BuildInsn(MOP_xadrp, stAddrOpnd, stOpnd);
8359 rematInsns.emplace_back(&insn);
8360 return *CreateMemOperand(MemOperand::kAddrModeLo12Li, size, stAddrOpnd, nullptr,
8361 &GetOrCreateOfstOpnd(static_cast<uint64>(offset), k32BitSize), &symbol);
8362 }
8363 }
8364 } else {
8365 CHECK_FATAL(false, "NYI");
8366 }
8367 }
8368
GetOrCreateMemOpnd(const MIRSymbol & symbol,int64 offset,uint32 size,bool forLocalRef,bool needLow12,RegOperand * regOp)8369 MemOperand &AArch64CGFunc::GetOrCreateMemOpnd(const MIRSymbol &symbol, int64 offset, uint32 size, bool forLocalRef,
8370 bool needLow12, RegOperand *regOp)
8371 {
8372 MIRStorageClass storageClass = symbol.GetStorageClass();
8373 if ((storageClass == kScAuto) || (storageClass == kScFormal)) {
8374 AArch64SymbolAlloc *symLoc =
8375 static_cast<AArch64SymbolAlloc *>(GetMemlayout()->GetSymAllocInfo(symbol.GetStIndex()));
8376 if (forLocalRef) {
8377 auto p = GetMemlayout()->GetLocalRefLocMap().find(symbol.GetStIdx());
8378 CHECK_FATAL(p != GetMemlayout()->GetLocalRefLocMap().end(), "sym loc should have been defined");
8379 symLoc = static_cast<AArch64SymbolAlloc *>(p->second);
8380 }
8381 DEBUG_ASSERT(symLoc != nullptr, "sym loc should have been defined");
8382 /* At this point, we don't know which registers the callee needs to save. */
8383 DEBUG_ASSERT((IsFPLRAddedToCalleeSavedList() || (SizeOfCalleeSaved() == 0)),
8384 "CalleeSaved won't be known until after Register Allocation");
8385 StIdx idx = symbol.GetStIdx();
8386 auto it = memOpndsRequiringOffsetAdjustment.find(idx);
8387 DEBUG_ASSERT((!IsFPLRAddedToCalleeSavedList() ||
8388 ((it != memOpndsRequiringOffsetAdjustment.end()) || (storageClass == kScFormal))),
8389 "Memory operand of this symbol should have been added to the hash table");
8390 int32 stOffset = GetBaseOffset(*symLoc);
8391 if (it != memOpndsRequiringOffsetAdjustment.end()) {
8392 if (GetMemlayout()->IsLocalRefLoc(symbol)) {
8393 if (!forLocalRef) {
8394 return *(it->second);
8395 }
8396 } else {
8397 Operand *offOpnd = (it->second)->GetOffset();
8398 DEBUG_ASSERT(offOpnd != nullptr, "offOpnd should not be nullptr");
8399 if (((static_cast<OfstOperand *>(offOpnd))->GetOffsetValue() == (stOffset + offset)) &&
8400 (it->second->GetSize() == size)) {
8401 return *(it->second);
8402 }
8403 }
8404 }
8405 it = memOpndsForStkPassedArguments.find(idx);
8406 if (it != memOpndsForStkPassedArguments.end()) {
8407 if (GetMemlayout()->IsLocalRefLoc(symbol)) {
8408 if (!forLocalRef) {
8409 return *(it->second);
8410 }
8411 } else {
8412 return *(it->second);
8413 }
8414 }
8415
8416 RegOperand *baseOpnd = static_cast<RegOperand *>(GetBaseReg(*symLoc));
8417 int32 totalOffset = stOffset + static_cast<int32>(offset);
8418 /* needs a fresh copy of ImmOperand as we may adjust its offset at a later stage. */
8419 OfstOperand *offsetOpnd = nullptr;
8420 if (CGOptions::IsBigEndian()) {
8421 if (symLoc->GetMemSegment()->GetMemSegmentKind() == kMsArgsStkPassed && size < k64BitSize) {
8422 offsetOpnd = &CreateOfstOpnd(k4BitSize + static_cast<uint32>(totalOffset), k64BitSize);
8423 } else {
8424 offsetOpnd = &CreateOfstOpnd(static_cast<uint64>(static_cast<int64>(totalOffset)), k64BitSize);
8425 }
8426 } else {
8427 offsetOpnd = &CreateOfstOpnd(static_cast<uint64>(static_cast<int64>(totalOffset)), k64BitSize);
8428 }
8429 if (symLoc->GetMemSegment()->GetMemSegmentKind() == kMsArgsStkPassed &&
8430 MemOperand::IsPIMMOffsetOutOfRange(totalOffset, size)) {
8431 ImmOperand *offsetOprand = &CreateImmOperand(totalOffset, k64BitSize, true, kUnAdjustVary);
8432 Operand *resImmOpnd = &SelectCopy(*offsetOprand, PTY_i64, PTY_i64);
8433 return *CreateMemOperand(MemOperand::kAddrModeBOrX, size, *baseOpnd, static_cast<RegOperand &>(*resImmOpnd),
8434 nullptr, symbol, true);
8435 } else {
8436 if (symLoc->GetMemSegment()->GetMemSegmentKind() == kMsArgsStkPassed) {
8437 offsetOpnd->SetVary(kUnAdjustVary);
8438 }
8439 MemOperand *res = CreateMemOperand(MemOperand::kAddrModeBOi, size, *baseOpnd, nullptr, offsetOpnd, &symbol);
8440 if ((symbol.GetType()->GetKind() != kTypeClass) && !forLocalRef) {
8441 memOpndsRequiringOffsetAdjustment[idx] = res;
8442 }
8443 return *res;
8444 }
8445 } else if ((storageClass == kScGlobal) || (storageClass == kScExtern)) {
8446 StImmOperand &stOpnd = CreateStImmOperand(symbol, offset, 0);
8447 if (!regOp) {
8448 regOp = static_cast<RegOperand *>(&CreateRegisterOperandOfType(PTY_u64));
8449 }
8450 RegOperand &stAddrOpnd = *regOp;
8451 SelectAddrof(stAddrOpnd, stOpnd);
8452 /* MemOperand::AddrMode_B_OI */
8453 return *CreateMemOperand(MemOperand::kAddrModeBOi, size, stAddrOpnd, nullptr,
8454 &GetOrCreateOfstOpnd(0, k32BitSize), &symbol);
8455 } else if ((storageClass == kScPstatic) || (storageClass == kScFstatic)) {
8456 return CreateMemOpndForStatic(symbol, offset, size, needLow12, regOp);
8457 } else {
8458 CHECK_FATAL(false, "NYI");
8459 }
8460 }
8461
CreateMemOpndForStatic(const MIRSymbol & symbol,int64 offset,uint32 size,bool needLow12,RegOperand * regOp)8462 MemOperand &AArch64CGFunc::CreateMemOpndForStatic(const MIRSymbol &symbol, int64 offset, uint32 size, bool needLow12,
8463 RegOperand *regOp)
8464 {
8465 if (symbol.GetSKind() == kStConst) {
8466 DEBUG_ASSERT(offset == 0, "offset should be 0 for constant literals");
8467 return *CreateMemOperand(MemOperand::kAddrModeLiteral, size, symbol);
8468 } else {
8469 /* not guaranteed align for uninitialized symbol */
8470 if (needLow12 || (!symbol.IsConst() && CGOptions::IsPIC())) {
8471 StImmOperand &stOpnd = CreateStImmOperand(symbol, offset, 0);
8472 if (!regOp) {
8473 regOp = static_cast<RegOperand *>(&CreateRegisterOperandOfType(PTY_u64));
8474 }
8475 RegOperand &stAddrOpnd = *regOp;
8476 SelectAddrof(stAddrOpnd, stOpnd);
8477 return *CreateMemOperand(MemOperand::kAddrModeBOi, size, stAddrOpnd, nullptr,
8478 &GetOrCreateOfstOpnd(0, k32BitSize), &symbol);
8479 } else {
8480 StImmOperand &stOpnd = CreateStImmOperand(symbol, offset, 0);
8481 if (!regOp) {
8482 regOp = static_cast<RegOperand *>(&CreateRegisterOperandOfType(PTY_u64));
8483 }
8484 RegOperand &stAddrOpnd = *regOp;
8485 Insn &insn = GetInsnBuilder()->BuildInsn(MOP_xadrp, stAddrOpnd, stOpnd);
8486 GetCurBB()->AppendInsn(insn);
8487 if (GetCG()->GetOptimizeLevel() == CGOptions::kLevel0 ||
8488 ((size == k64BitSize) && (offset % static_cast<int64>(k8BitSizeInt) != 0)) ||
8489 ((size == k32BitSize) && (offset % static_cast<int64>(k4BitSizeInt) != 0)) ||
8490 ((size == k16BitSize) && (offset % static_cast<int64>(k2BitSizeInt) != 0))) {
8491 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xadrpl12, stAddrOpnd, stAddrOpnd, stOpnd));
8492 return *CreateMemOperand(MemOperand::kAddrModeBOi, size, stAddrOpnd, nullptr,
8493 &GetOrCreateOfstOpnd(static_cast<uint64>(0), k32BitSize), nullptr);
8494 }
8495
8496 return *CreateMemOperand(MemOperand::kAddrModeLo12Li, size, stAddrOpnd, nullptr,
8497 &GetOrCreateOfstOpnd(static_cast<uint64>(offset), k32BitSize), &symbol);
8498 }
8499 }
8500 }
8501
HashMemOpnd(MemOperand & tMemOpnd)8502 MemOperand &AArch64CGFunc::HashMemOpnd(MemOperand &tMemOpnd)
8503 {
8504 auto it = hashMemOpndTable.find(tMemOpnd);
8505 if (it != hashMemOpndTable.end()) {
8506 return *(it->second);
8507 }
8508 auto *res = memPool->New<MemOperand>(tMemOpnd);
8509 hashMemOpndTable[tMemOpnd] = res;
8510 return *res;
8511 }
8512
GetOrCreateMemOpnd(MemOperand::AArch64AddressingMode mode,uint32 size,RegOperand * base,RegOperand * index,ImmOperand * offset,const MIRSymbol * st)8513 MemOperand &AArch64CGFunc::GetOrCreateMemOpnd(MemOperand::AArch64AddressingMode mode, uint32 size, RegOperand *base,
8514 RegOperand *index, ImmOperand *offset, const MIRSymbol *st)
8515 {
8516 DEBUG_ASSERT(base != nullptr, "nullptr check");
8517 MemOperand tMemOpnd(mode, size, *base, index, offset, st);
8518 if (base->GetRegisterNumber() == RFP || base->GetRegisterNumber() == RSP) {
8519 tMemOpnd.SetStackMem(true);
8520 }
8521 return HashMemOpnd(tMemOpnd);
8522 }
8523
GetOrCreateMemOpnd(MemOperand::AArch64AddressingMode mode,uint32 size,RegOperand * base,RegOperand * index,int32 shift,bool isSigned)8524 MemOperand &AArch64CGFunc::GetOrCreateMemOpnd(MemOperand::AArch64AddressingMode mode, uint32 size, RegOperand *base,
8525 RegOperand *index, int32 shift, bool isSigned)
8526 {
8527 DEBUG_ASSERT(base != nullptr, "nullptr check");
8528 MemOperand tMemOpnd(mode, size, *base, *index, shift, isSigned);
8529 if (base->GetRegisterNumber() == RFP || base->GetRegisterNumber() == RSP) {
8530 tMemOpnd.SetStackMem(true);
8531 }
8532 return HashMemOpnd(tMemOpnd);
8533 }
8534
GetOrCreateMemOpnd(MemOperand & oldMem)8535 MemOperand &AArch64CGFunc::GetOrCreateMemOpnd(MemOperand &oldMem)
8536 {
8537 return HashMemOpnd(oldMem);
8538 }
8539
8540 /* offset: base offset from FP or SP */
CreateMemOpnd(RegOperand & baseOpnd,int64 offset,uint32 size)8541 MemOperand &AArch64CGFunc::CreateMemOpnd(RegOperand &baseOpnd, int64 offset, uint32 size)
8542 {
8543 OfstOperand &offsetOpnd = CreateOfstOpnd(static_cast<uint64>(offset), k32BitSize);
8544 /* do not need to check bit size rotate of sign immediate */
8545 bool checkSimm = (offset > kMinSimm64 && offset < kMaxSimm64Pair);
8546 if (!checkSimm && !ImmOperand::IsInBitSizeRot(kMaxImmVal12Bits, offset)) {
8547 Operand *resImmOpnd = &SelectCopy(CreateImmOperand(offset, k32BitSize, true), PTY_i32, PTY_i32);
8548 return *CreateMemOperand(MemOperand::kAddrModeBOrX, size, baseOpnd, static_cast<RegOperand *>(resImmOpnd),
8549 nullptr, nullptr);
8550 } else {
8551 return *CreateMemOperand(MemOperand::kAddrModeBOi, size, baseOpnd, nullptr, &offsetOpnd, nullptr);
8552 }
8553 }
8554
8555 /* offset: base offset + #:lo12:Label+immediate */
CreateMemOpnd(RegOperand & baseOpnd,int64 offset,uint32 size,const MIRSymbol & sym)8556 MemOperand &AArch64CGFunc::CreateMemOpnd(RegOperand &baseOpnd, int64 offset, uint32 size, const MIRSymbol &sym)
8557 {
8558 OfstOperand &offsetOpnd = CreateOfstOpnd(static_cast<uint64>(offset), k32BitSize);
8559 DEBUG_ASSERT(ImmOperand::IsInBitSizeRot(kMaxImmVal12Bits, offset), "");
8560 return *CreateMemOperand(MemOperand::kAddrModeBOi, size, baseOpnd, nullptr, &offsetOpnd, &sym);
8561 }
8562
GenStructParamIndex(RegOperand & base,const BaseNode & indexExpr,int shift,PrimType baseType)8563 RegOperand &AArch64CGFunc::GenStructParamIndex(RegOperand &base, const BaseNode &indexExpr, int shift,
8564 PrimType baseType)
8565 {
8566 RegOperand *index = &LoadIntoRegister(*HandleExpr(indexExpr, *(indexExpr.Opnd(0))), PTY_a64);
8567 RegOperand *srcOpnd = &CreateRegisterOperandOfType(PTY_a64);
8568 ImmOperand *imm = &CreateImmOperand(PTY_a64, shift);
8569 SelectShift(*srcOpnd, *index, *imm, kShiftLeft, PTY_a64);
8570 RegOperand *result = &CreateRegisterOperandOfType(PTY_a64);
8571 SelectAdd(*result, base, *srcOpnd, PTY_a64);
8572
8573 OfstOperand *offopnd = &CreateOfstOpnd(0, k32BitSize);
8574 MemOperand &mo = GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, k64BitSize, result, nullptr, offopnd, nullptr);
8575 RegOperand &structAddr = CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
8576 GetCurBB()->AppendInsn(
8577 GetInsnBuilder()->BuildInsn(PickLdInsn(GetPrimTypeBitSize(baseType), baseType), structAddr, mo));
8578 return structAddr;
8579 }
8580
8581 /*
8582 * case 1: iread a64 <* <* void>> 0 (add a64 (
8583 * addrof a64 $__reg_jni_func_tab$$libcore_all_bytecode,
8584 * mul a64 (
8585 * cvt a64 i32 (constval i32 21),
8586 * constval a64 8)))
8587 *
8588 * case 2 : iread u32 <* u8> 0 (add a64 (regread a64 %61, constval a64 3))
8589 * case 3 : iread u32 <* u8> 0 (add a64 (regread a64 %61, regread a64 %65))
8590 * case 4 : iread u32 <* u8> 0 (add a64 (cvt a64 i32(regread %n)))
8591 */
CheckAndCreateExtendMemOpnd(PrimType ptype,const BaseNode & addrExpr,int64 offset,AArch64isa::MemoryOrdering memOrd)8592 MemOperand *AArch64CGFunc::CheckAndCreateExtendMemOpnd(PrimType ptype, const BaseNode &addrExpr, int64 offset,
8593 AArch64isa::MemoryOrdering memOrd)
8594 {
8595 aggParamReg = nullptr;
8596 if (memOrd != AArch64isa::kMoNone || addrExpr.GetOpCode() != OP_add || offset != 0) {
8597 return nullptr;
8598 }
8599 BaseNode *baseExpr = addrExpr.Opnd(0);
8600 BaseNode *addendExpr = addrExpr.Opnd(1);
8601
8602 if (baseExpr->GetOpCode() == OP_regread) {
8603 /* case 2 */
8604 if (addendExpr->GetOpCode() == OP_constval) {
8605 DEBUG_ASSERT(addrExpr.GetNumOpnds() == kOpndNum2, "Unepect expr operand in CheckAndCreateExtendMemOpnd");
8606 ConstvalNode *constOfstNode = static_cast<ConstvalNode *>(addendExpr);
8607 DEBUG_ASSERT(constOfstNode->GetConstVal()->GetKind() == kConstInt, "expect MIRIntConst");
8608 MIRIntConst *intOfst = safe_cast<MIRIntConst>(constOfstNode->GetConstVal());
8609 CHECK_FATAL(intOfst != nullptr, "just checking");
8610 /* discard large offset and negative offset */
8611 if (intOfst->GetExtValue() > INT32_MAX || intOfst->IsNegative()) {
8612 return nullptr;
8613 }
8614 uint32 scale = static_cast<uint32>(intOfst->GetExtValue());
8615 OfstOperand &ofstOpnd = GetOrCreateOfstOpnd(scale, k32BitSize);
8616 uint32 dsize = GetPrimTypeBitSize(ptype);
8617 MemOperand *memOpnd =
8618 &GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, GetPrimTypeBitSize(ptype),
8619 SelectRegread(*static_cast<RegreadNode *>(baseExpr)), nullptr, &ofstOpnd, nullptr);
8620 return IsOperandImmValid(PickLdInsn(dsize, ptype), memOpnd, kInsnSecondOpnd) ? memOpnd : nullptr;
8621 /* case 3 */
8622 } else if (addendExpr->GetOpCode() == OP_regread) {
8623 CHECK_FATAL(addrExpr.GetNumOpnds() == kOpndNum2, "Unepect expr operand in CheckAndCreateExtendMemOpnd");
8624 if (GetPrimTypeSize(baseExpr->GetPrimType()) != GetPrimTypeSize(addendExpr->GetPrimType())) {
8625 return nullptr;
8626 }
8627
8628 auto *baseReg = SelectRegread(*static_cast<RegreadNode *>(baseExpr));
8629 auto *indexReg = SelectRegread(*static_cast<RegreadNode *>(addendExpr));
8630 MemOperand *memOpnd = &GetOrCreateMemOpnd(MemOperand::kAddrModeBOrX, GetPrimTypeBitSize(ptype), baseReg,
8631 indexReg, nullptr, nullptr);
8632 if (CGOptions::IsArm64ilp32() && IsSignedInteger(addendExpr->GetPrimType())) {
8633 memOpnd->SetExtend(memOpnd->GetExtend() | MemOperand::ExtendInfo::kSignExtend);
8634 }
8635 return memOpnd;
8636 /* case 4 */
8637 } else if (addendExpr->GetOpCode() == OP_cvt && addendExpr->GetNumOpnds() == 1) {
8638 int shiftAmount = 0;
8639 BaseNode *cvtRegreadNode = addendExpr->Opnd(kInsnFirstOpnd);
8640 if (cvtRegreadNode->GetOpCode() == OP_regread && cvtRegreadNode->IsLeaf()) {
8641 uint32 fromSize = GetPrimTypeBitSize(cvtRegreadNode->GetPrimType());
8642 uint32 toSize = GetPrimTypeBitSize(addendExpr->GetPrimType());
8643 if (toSize < fromSize) {
8644 return nullptr;
8645 }
8646
8647 MemOperand *memOpnd = &GetOrCreateMemOpnd(MemOperand::kAddrModeBOrX, GetPrimTypeBitSize(ptype),
8648 SelectRegread(*static_cast<RegreadNode *>(baseExpr)),
8649 SelectRegread(*static_cast<RegreadNode *>(cvtRegreadNode)),
8650 shiftAmount, toSize != fromSize);
8651 return memOpnd;
8652 }
8653 }
8654 }
8655 if (addendExpr->GetOpCode() != OP_mul || !IsPrimitiveInteger(ptype)) {
8656 return nullptr;
8657 }
8658 BaseNode *indexExpr, *scaleExpr;
8659 indexExpr = addendExpr->Opnd(0);
8660 scaleExpr = addendExpr->Opnd(1);
8661 if (scaleExpr->GetOpCode() != OP_constval) {
8662 return nullptr;
8663 }
8664 ConstvalNode *constValNode = static_cast<ConstvalNode *>(scaleExpr);
8665 CHECK_FATAL(constValNode->GetConstVal()->GetKind() == kConstInt, "expect MIRIntConst");
8666 MIRIntConst *mirIntConst = safe_cast<MIRIntConst>(constValNode->GetConstVal());
8667 CHECK_FATAL(mirIntConst != nullptr, "just checking");
8668 int32 scale = mirIntConst->GetExtValue();
8669 if (scale < 0) {
8670 return nullptr;
8671 }
8672 uint32 unsignedScale = static_cast<uint32>(scale);
8673 if (unsignedScale != GetPrimTypeSize(ptype) || indexExpr->GetOpCode() != OP_cvt) {
8674 return nullptr;
8675 }
8676 /* 8 is 1 << 3; 4 is 1 << 2; 2 is 1 << 1; 1 is 1 << 0 */
8677 int32 shift = (unsignedScale == 8) ? 3 : ((unsignedScale == 4) ? 2 : ((unsignedScale == 2) ? 1 : 0));
8678 RegOperand &base = static_cast<RegOperand &>(LoadIntoRegister(*HandleExpr(addrExpr, *baseExpr), PTY_a64));
8679 TypeCvtNode *typeCvtNode = static_cast<TypeCvtNode *>(indexExpr);
8680 PrimType fromType = typeCvtNode->FromType();
8681 PrimType toType = typeCvtNode->GetPrimType();
8682 if (isAggParamInReg) {
8683 aggParamReg = &GenStructParamIndex(base, *indexExpr, shift, ptype);
8684 return nullptr;
8685 }
8686 MemOperand *memOpnd = nullptr;
8687 if ((fromType == PTY_i32) && (toType == PTY_a64)) {
8688 RegOperand &index =
8689 static_cast<RegOperand &>(LoadIntoRegister(*HandleExpr(*indexExpr, *indexExpr->Opnd(0)), PTY_i32));
8690 memOpnd = &GetOrCreateMemOpnd(MemOperand::kAddrModeBOrX, GetPrimTypeBitSize(ptype), &base, &index, shift, true);
8691 } else if ((fromType == PTY_u32) && (toType == PTY_a64)) {
8692 RegOperand &index =
8693 static_cast<RegOperand &>(LoadIntoRegister(*HandleExpr(*indexExpr, *indexExpr->Opnd(0)), PTY_u32));
8694 memOpnd =
8695 &GetOrCreateMemOpnd(MemOperand::kAddrModeBOrX, GetPrimTypeBitSize(ptype), &base, &index, shift, false);
8696 }
8697 return memOpnd;
8698 }
8699
CreateNonExtendMemOpnd(PrimType ptype,const BaseNode & parent,BaseNode & addrExpr,int64 offset)8700 MemOperand &AArch64CGFunc::CreateNonExtendMemOpnd(PrimType ptype, const BaseNode &parent, BaseNode &addrExpr,
8701 int64 offset)
8702 {
8703 Operand *addrOpnd = nullptr;
8704 if ((addrExpr.GetOpCode() == OP_add || addrExpr.GetOpCode() == OP_sub) &&
8705 addrExpr.Opnd(1)->GetOpCode() == OP_constval) {
8706 addrOpnd = HandleExpr(addrExpr, *addrExpr.Opnd(0));
8707 ConstvalNode *constOfstNode = static_cast<ConstvalNode *>(addrExpr.Opnd(1));
8708 DEBUG_ASSERT(constOfstNode->GetConstVal()->GetKind() == kConstInt, "expect MIRIntConst");
8709 MIRIntConst *intOfst = safe_cast<MIRIntConst>(constOfstNode->GetConstVal());
8710 CHECK_FATAL(intOfst != nullptr, "just checking");
8711 offset = (addrExpr.GetOpCode() == OP_add) ? offset + intOfst->GetSXTValue() : offset - intOfst->GetSXTValue();
8712 } else {
8713 addrOpnd = HandleExpr(parent, addrExpr);
8714 }
8715 addrOpnd = static_cast<RegOperand *>(&LoadIntoRegister(*addrOpnd, PTY_a64));
8716 Insn *lastInsn = GetCurBB() == nullptr ? nullptr : GetCurBB()->GetLastMachineInsn();
8717 if ((addrExpr.GetOpCode() == OP_CG_array_elem_add) && (offset == 0) && lastInsn &&
8718 (lastInsn->GetMachineOpcode() == MOP_xadrpl12) &&
8719 (&lastInsn->GetOperand(kInsnFirstOpnd) == &lastInsn->GetOperand(kInsnSecondOpnd))) {
8720 Operand &opnd = lastInsn->GetOperand(kInsnThirdOpnd);
8721 StImmOperand &stOpnd = static_cast<StImmOperand &>(opnd);
8722
8723 OfstOperand &ofstOpnd = GetOrCreateOfstOpnd(static_cast<uint64>(stOpnd.GetOffset()), k32BitSize);
8724 MemOperand &tmpMemOpnd =
8725 GetOrCreateMemOpnd(MemOperand::kAddrModeLo12Li, GetPrimTypeBitSize(ptype),
8726 static_cast<RegOperand *>(addrOpnd), nullptr, &ofstOpnd, stOpnd.GetSymbol());
8727 if (GetCurBB() && GetCurBB()->GetLastMachineInsn()) {
8728 GetCurBB()->RemoveInsn(*GetCurBB()->GetLastMachineInsn());
8729 }
8730 return tmpMemOpnd;
8731 } else {
8732 OfstOperand &ofstOpnd = GetOrCreateOfstOpnd(static_cast<uint64>(offset), k64BitSize);
8733 return GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, GetPrimTypeBitSize(ptype),
8734 static_cast<RegOperand *>(addrOpnd), nullptr, &ofstOpnd, nullptr);
8735 }
8736 }
8737
8738 /*
8739 * Create a memory operand with specified data type and memory ordering, making
8740 * use of aarch64 extend register addressing mode when possible.
8741 */
CreateMemOpnd(PrimType ptype,const BaseNode & parent,BaseNode & addrExpr,int64 offset,AArch64isa::MemoryOrdering memOrd)8742 MemOperand &AArch64CGFunc::CreateMemOpnd(PrimType ptype, const BaseNode &parent, BaseNode &addrExpr, int64 offset,
8743 AArch64isa::MemoryOrdering memOrd)
8744 {
8745 MemOperand *memOpnd = CheckAndCreateExtendMemOpnd(ptype, addrExpr, offset, memOrd);
8746 if (memOpnd != nullptr) {
8747 return *memOpnd;
8748 }
8749 return CreateNonExtendMemOpnd(ptype, parent, addrExpr, offset);
8750 }
8751
CreateMemOpndOrNull(PrimType ptype,const BaseNode & parent,BaseNode & addrExpr,int64 offset,AArch64isa::MemoryOrdering memOrd)8752 MemOperand *AArch64CGFunc::CreateMemOpndOrNull(PrimType ptype, const BaseNode &parent, BaseNode &addrExpr, int64 offset,
8753 AArch64isa::MemoryOrdering memOrd)
8754 {
8755 MemOperand *memOpnd = CheckAndCreateExtendMemOpnd(ptype, addrExpr, offset, memOrd);
8756 if (memOpnd != nullptr) {
8757 return memOpnd;
8758 } else if (aggParamReg != nullptr) {
8759 return nullptr;
8760 }
8761 return &CreateNonExtendMemOpnd(ptype, parent, addrExpr, offset);
8762 }
8763
GetOrCreateFuncNameOpnd(const MIRSymbol & symbol) const8764 Operand &AArch64CGFunc::GetOrCreateFuncNameOpnd(const MIRSymbol &symbol) const
8765 {
8766 return *memPool->New<FuncNameOperand>(symbol);
8767 }
8768
GetOrCreateRflag()8769 Operand &AArch64CGFunc::GetOrCreateRflag()
8770 {
8771 if (rcc == nullptr) {
8772 rcc = &CreateRflagOperand();
8773 }
8774 return *rcc;
8775 }
8776
GetRflag() const8777 const Operand *AArch64CGFunc::GetRflag() const
8778 {
8779 return rcc;
8780 }
8781
GetOrCreatevaryreg()8782 RegOperand &AArch64CGFunc::GetOrCreatevaryreg()
8783 {
8784 if (vary == nullptr) {
8785 regno_t vRegNO = NewVReg(kRegTyVary, k8ByteSize);
8786 vary = &CreateVirtualRegisterOperand(vRegNO);
8787 }
8788 return *vary;
8789 }
8790
8791 /* the first operand in opndvec is return opnd */
SelectLibCall(const std::string & funcName,std::vector<Operand * > & opndVec,PrimType primType,PrimType retPrimType,bool is2ndRet)8792 void AArch64CGFunc::SelectLibCall(const std::string &funcName, std::vector<Operand *> &opndVec, PrimType primType,
8793 PrimType retPrimType, bool is2ndRet)
8794 {
8795 std::vector<PrimType> pt;
8796 pt.push_back(retPrimType);
8797 for (size_t i = 0; i < opndVec.size(); ++i) {
8798 pt.push_back(primType);
8799 }
8800 SelectLibCallNArg(funcName, opndVec, pt, retPrimType, is2ndRet);
8801 return;
8802 }
8803
SelectLibCallNArg(const std::string & funcName,std::vector<Operand * > & opndVec,std::vector<PrimType> pt,PrimType retPrimType,bool is2ndRet)8804 void AArch64CGFunc::SelectLibCallNArg(const std::string &funcName, std::vector<Operand *> &opndVec,
8805 std::vector<PrimType> pt, PrimType retPrimType, bool is2ndRet)
8806 {
8807 std::string newName = funcName;
8808 // Check whether we have a maple version of libcall and we want to use it instead.
8809 if (!CGOptions::IsDuplicateAsmFileEmpty() && asmMap.find(funcName) != asmMap.end()) {
8810 newName = asmMap.at(funcName);
8811 }
8812 MIRSymbol *st = GlobalTables::GetGsymTable().CreateSymbol(kScopeGlobal);
8813 st->SetNameStrIdx(newName);
8814 st->SetStorageClass(kScExtern);
8815 st->SetSKind(kStFunc);
8816 /* setup the type of the callee function */
8817 std::vector<TyIdx> vec;
8818 std::vector<TypeAttrs> vecAt;
8819 for (size_t i = 1; i < opndVec.size(); ++i) {
8820 (void)vec.emplace_back(GlobalTables::GetTypeTable().GetTypeTable()[static_cast<size_t>(pt[i])]->GetTypeIndex());
8821 vecAt.emplace_back(TypeAttrs());
8822 }
8823
8824 MIRType *retType = GlobalTables::GetTypeTable().GetTypeTable().at(static_cast<size_t>(retPrimType));
8825 st->SetTyIdx(GetBecommon().BeGetOrCreateFunctionType(retType->GetTypeIndex(), vec, vecAt)->GetTypeIndex());
8826
8827 if (GetCG()->GenerateVerboseCG()) {
8828 const std::string &comment = "lib call : " + newName;
8829 GetCurBB()->AppendInsn(CreateCommentInsn(comment));
8830 }
8831 // only create c lib call here
8832 AArch64CallConvImpl parmLocator(GetBecommon());
8833 CCLocInfo ploc;
8834 /* setup actual parameters */
8835 ListOperand *srcOpnds = CreateListOpnd(*GetFuncScopeAllocator());
8836 for (size_t i = 1; i < opndVec.size(); ++i) {
8837 DEBUG_ASSERT(pt[i] != PTY_void, "primType check");
8838 MIRType *ty = GlobalTables::GetTypeTable().GetTypeTable()[static_cast<size_t>(pt[i])];
8839 Operand *stOpnd = opndVec[i];
8840 if (stOpnd->GetKind() != Operand::kOpdRegister) {
8841 stOpnd = &SelectCopy(*stOpnd, pt[i], pt[i]);
8842 }
8843 RegOperand *expRegOpnd = static_cast<RegOperand *>(stOpnd);
8844 parmLocator.LocateNextParm(*ty, ploc);
8845 if (ploc.reg0 != 0) { /* load to the register */
8846 RegOperand &parmRegOpnd = GetOrCreatePhysicalRegisterOperand(
8847 static_cast<AArch64reg>(ploc.reg0), expRegOpnd->GetSize(), GetRegTyFromPrimTy(pt[i]));
8848 SelectCopy(parmRegOpnd, pt[i], *expRegOpnd, pt[i]);
8849 srcOpnds->PushOpnd(parmRegOpnd);
8850 }
8851 DEBUG_ASSERT(ploc.reg1 == 0, "SelectCall NYI");
8852 }
8853
8854 MIRSymbol *sym = GetFunction().GetLocalOrGlobalSymbol(st->GetStIdx(), false);
8855 Insn &callInsn = AppendCall(*sym, *srcOpnds);
8856 MIRType *callRetType = GlobalTables::GetTypeTable().GetTypeTable().at(static_cast<int32>(retPrimType));
8857 if (callRetType != nullptr) {
8858 callInsn.SetRetSize(static_cast<uint32>(callRetType->GetSize()));
8859 callInsn.SetIsCallReturnUnsigned(IsUnsignedInteger(callRetType->GetPrimType()));
8860 }
8861 GetFunction().SetHasCall();
8862 /* get return value */
8863 Operand *opnd0 = opndVec[0];
8864 CCLocInfo retMech;
8865 parmLocator.LocateRetVal(*(GlobalTables::GetTypeTable().GetTypeTable().at(retPrimType)), retMech);
8866 if (retMech.GetRegCount() <= 0) {
8867 CHECK_FATAL(false, "should return from register");
8868 }
8869 if (!opnd0->IsRegister()) {
8870 CHECK_FATAL(false, "nyi");
8871 }
8872 RegOperand *regOpnd = static_cast<RegOperand *>(opnd0);
8873 AArch64reg regNum = static_cast<AArch64reg>(is2ndRet ? retMech.GetReg1() : retMech.GetReg0());
8874 if (regOpnd->GetRegisterNumber() != regNum) {
8875 RegOperand &retOpnd =
8876 GetOrCreatePhysicalRegisterOperand(regNum, regOpnd->GetSize(), GetRegTyFromPrimTy(retPrimType));
8877 SelectCopy(*opnd0, retPrimType, retOpnd, retPrimType);
8878 }
8879 }
8880
GetBaseReg(const SymbolAlloc & symAlloc)8881 RegOperand *AArch64CGFunc::GetBaseReg(const SymbolAlloc &symAlloc)
8882 {
8883 MemSegmentKind sgKind = symAlloc.GetMemSegment()->GetMemSegmentKind();
8884 DEBUG_ASSERT(((sgKind == kMsArgsRegPassed) || (sgKind == kMsLocals) || (sgKind == kMsRefLocals) ||
8885 (sgKind == kMsArgsToStkPass) || (sgKind == kMsArgsStkPassed)),
8886 "NYI");
8887
8888 if (sgKind == kMsArgsStkPassed || sgKind == kMsCold) {
8889 return &GetOrCreatevaryreg();
8890 }
8891
8892 if (fsp == nullptr) {
8893 fsp = &GetOrCreatePhysicalRegisterOperand(RFP, GetPointerSize() * kBitsPerByte, kRegTyInt);
8894 }
8895 return fsp;
8896 }
8897
GetBaseOffset(const SymbolAlloc & symbolAlloc)8898 int32 AArch64CGFunc::GetBaseOffset(const SymbolAlloc &symbolAlloc)
8899 {
8900
8901 const AArch64SymbolAlloc *symAlloc = static_cast<const AArch64SymbolAlloc *>(&symbolAlloc);
8902 // Call Frame layout of AArch64
8903 // Refer to V2 in aarch64_memlayout.h.
8904 // Do Not change this unless you know what you do
8905 // O2 mode refer to V2.1 in aarch64_memlayout.cpp
8906 const int32 sizeofFplr = static_cast<int32>(2 * kAarch64IntregBytelen);
8907 MemSegmentKind sgKind = symAlloc->GetMemSegment()->GetMemSegmentKind();
8908 AArch64MemLayout *memLayout = static_cast<AArch64MemLayout *>(this->GetMemlayout());
8909 if (sgKind == kMsArgsStkPassed) { /* for callees */
8910 int32 offset = static_cast<int32>(symAlloc->GetOffset());
8911 offset += static_cast<int32>(memLayout->GetSizeOfColdToStk());
8912 return offset;
8913 } else if (sgKind == kMsCold) {
8914 int offset = static_cast<int32>(symAlloc->GetOffset());
8915 return offset;
8916 } else if (sgKind == kMsArgsRegPassed) {
8917 int32 baseOffset;
8918 if (GetCG()->IsLmbc()) {
8919 baseOffset = static_cast<int32>(symAlloc->GetOffset()) +
8920 static_cast<int32>(memLayout->GetSizeOfRefLocals() +
8921 memLayout->SizeOfArgsToStackPass()); /* SP relative */
8922 } else {
8923 baseOffset = static_cast<int32>(memLayout->GetSizeOfLocals() + memLayout->GetSizeOfRefLocals()) +
8924 static_cast<int32>(symAlloc->GetOffset());
8925 }
8926 return baseOffset + sizeofFplr;
8927 } else if (sgKind == kMsRefLocals) {
8928 int32 baseOffset = static_cast<int32>(symAlloc->GetOffset()) + static_cast<int32>(memLayout->GetSizeOfLocals());
8929 return baseOffset + sizeofFplr;
8930 } else if (sgKind == kMsLocals) {
8931 if (GetCG()->IsLmbc()) {
8932 CHECK_FATAL(false, "invalid lmbc's locals");
8933 }
8934 int32 baseOffset = symAlloc->GetOffset();
8935 return baseOffset + sizeofFplr;
8936 } else if (sgKind == kMsSpillReg) {
8937 int32 baseOffset;
8938 if (GetCG()->IsLmbc()) {
8939 baseOffset = static_cast<int32>(symAlloc->GetOffset()) +
8940 static_cast<int32>(memLayout->SizeOfArgsRegisterPassed() + memLayout->GetSizeOfRefLocals() +
8941 memLayout->SizeOfArgsToStackPass());
8942 } else {
8943 baseOffset = static_cast<int32>(symAlloc->GetOffset()) +
8944 static_cast<int32>(memLayout->SizeOfArgsRegisterPassed() + memLayout->GetSizeOfLocals() +
8945 memLayout->GetSizeOfRefLocals());
8946 }
8947 return baseOffset + sizeofFplr;
8948 } else if (sgKind == kMsArgsToStkPass) { /* this is for callers */
8949 return static_cast<int32>(symAlloc->GetOffset());
8950 } else {
8951 CHECK_FATAL(false, "sgKind check");
8952 }
8953 return 0;
8954 }
8955
AppendCall(const MIRSymbol & funcSymbol)8956 void AArch64CGFunc::AppendCall(const MIRSymbol &funcSymbol)
8957 {
8958 ListOperand *srcOpnds = CreateListOpnd(*GetFuncScopeAllocator());
8959 AppendCall(funcSymbol, *srcOpnds);
8960 }
8961
DBGFixCallFrameLocationOffsets()8962 void AArch64CGFunc::DBGFixCallFrameLocationOffsets()
8963 {
8964 unsigned idx = 0;
8965 for (DBGExprLoc *el : GetDbgCallFrameLocations(true)) {
8966 if (el && el->GetSimpLoc() && el->GetSimpLoc()->GetDwOp() == DW_OP_fbreg) {
8967 SymbolAlloc *symloc = static_cast<SymbolAlloc *>(el->GetSymLoc());
8968 int32 offset = GetBaseOffset(*symloc) - ((idx < AArch64Abi::kNumIntParmRegs) ? GetDbgCallFrameOffset() : 0);
8969 el->SetFboffset(offset);
8970 }
8971 idx++;
8972 }
8973 for (DBGExprLoc *el : GetDbgCallFrameLocations(false)) {
8974 if (el->GetSimpLoc()->GetDwOp() == DW_OP_fbreg) {
8975 SymbolAlloc *symloc = static_cast<SymbolAlloc *>(el->GetSymLoc());
8976 int32 offset = GetBaseOffset(*symloc) - GetDbgCallFrameOffset();
8977 el->SetFboffset(offset);
8978 }
8979 }
8980 }
8981
SelectAddAfterInsn(Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType,bool isDest,Insn & insn)8982 void AArch64CGFunc::SelectAddAfterInsn(Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType, bool isDest,
8983 Insn &insn)
8984 {
8985 uint32 dsize = GetPrimTypeBitSize(primType);
8986 bool is64Bits = (dsize == k64BitSize);
8987 DEBUG_ASSERT(opnd0.GetKind() == Operand::kOpdRegister, "Spill memory operand should based on register");
8988 DEBUG_ASSERT((opnd1.GetKind() == Operand::kOpdImmediate || opnd1.GetKind() == Operand::kOpdOffset),
8989 "Spill memory operand should be with a immediate offset.");
8990
8991 ImmOperand *immOpnd = static_cast<ImmOperand *>(&opnd1);
8992
8993 MOperator mOpCode = MOP_undef;
8994 Insn *curInsn = &insn;
8995 /* lower 24 bits has 1, higher bits are all 0 */
8996 if (immOpnd->IsInBitSize(kMaxImmVal24Bits, 0)) {
8997 /* lower 12 bits and higher 12 bits both has 1 */
8998 Operand *newOpnd0 = &opnd0;
8999 if (!(immOpnd->IsInBitSize(kMaxImmVal12Bits, 0) || immOpnd->IsInBitSize(kMaxImmVal12Bits, kMaxImmVal12Bits))) {
9000 /* process higher 12 bits */
9001 ImmOperand &immOpnd2 =
9002 CreateImmOperand(static_cast<int64>(static_cast<uint64>(immOpnd->GetValue()) >> kMaxImmVal12Bits),
9003 immOpnd->GetSize(), immOpnd->IsSignedValue());
9004 mOpCode = is64Bits ? MOP_xaddrri24 : MOP_waddrri24;
9005 BitShiftOperand &shiftopnd = CreateBitShiftOperand(BitShiftOperand::kLSL, kShiftAmount12, k64BitSize);
9006 Insn &newInsn = GetInsnBuilder()->BuildInsn(mOpCode, resOpnd, opnd0, immOpnd2, shiftopnd);
9007 DEBUG_ASSERT(IsOperandImmValid(mOpCode, &immOpnd2, kInsnThirdOpnd), "immOpnd2 appears invalid");
9008 if (isDest) {
9009 insn.GetBB()->InsertInsnAfter(insn, newInsn);
9010 } else {
9011 insn.GetBB()->InsertInsnBefore(insn, newInsn);
9012 }
9013 /* get lower 12 bits value */
9014 immOpnd->ModuloByPow2(static_cast<int32>(kMaxImmVal12Bits));
9015 newOpnd0 = &resOpnd;
9016 curInsn = &newInsn;
9017 }
9018 /* process lower 12 bits value */
9019 mOpCode = is64Bits ? MOP_xaddrri12 : MOP_waddrri12;
9020 Insn &newInsn = GetInsnBuilder()->BuildInsn(mOpCode, resOpnd, *newOpnd0, *immOpnd);
9021 DEBUG_ASSERT(IsOperandImmValid(mOpCode, immOpnd, kInsnThirdOpnd), "immOpnd appears invalid");
9022 if (isDest) {
9023 insn.GetBB()->InsertInsnAfter(*curInsn, newInsn);
9024 } else {
9025 insn.GetBB()->InsertInsnBefore(insn, newInsn);
9026 }
9027 } else {
9028 /* load into register */
9029 RegOperand &movOpnd = GetOrCreatePhysicalRegisterOperand(R16, dsize, kRegTyInt);
9030 mOpCode = is64Bits ? MOP_xmovri64 : MOP_wmovri32;
9031 Insn &movInsn = GetInsnBuilder()->BuildInsn(mOpCode, movOpnd, *immOpnd);
9032 mOpCode = is64Bits ? MOP_xaddrrr : MOP_waddrrr;
9033 Insn &newInsn = GetInsnBuilder()->BuildInsn(mOpCode, resOpnd, opnd0, movOpnd);
9034 if (isDest) {
9035 (void)insn.GetBB()->InsertInsnAfter(insn, newInsn);
9036 (void)insn.GetBB()->InsertInsnAfter(insn, movInsn);
9037 } else {
9038 (void)insn.GetBB()->InsertInsnBefore(insn, movInsn);
9039 (void)insn.GetBB()->InsertInsnBefore(insn, newInsn);
9040 }
9041 }
9042 }
9043
AdjustMemOperandIfOffsetOutOfRange(MemOperand * memOpnd,regno_t vrNum,bool isDest,Insn & insn,AArch64reg regNum,bool & isOutOfRange)9044 MemOperand *AArch64CGFunc::AdjustMemOperandIfOffsetOutOfRange(MemOperand *memOpnd, regno_t vrNum, bool isDest,
9045 Insn &insn, AArch64reg regNum, bool &isOutOfRange)
9046 {
9047 if (vrNum >= vReg.VRegTableSize()) {
9048 CHECK_FATAL(false, "index out of range in AArch64CGFunc::AdjustMemOperandIfOffsetOutOfRange");
9049 }
9050 uint32 dataSize = GetOrCreateVirtualRegisterOperand(vrNum).GetSize();
9051 if (IsImmediateOffsetOutOfRange(*memOpnd, dataSize) && CheckIfSplitOffsetWithAdd(*memOpnd, dataSize)) {
9052 isOutOfRange = true;
9053 memOpnd = &SplitOffsetWithAddInstruction(*memOpnd, dataSize, regNum, isDest, &insn);
9054 } else {
9055 isOutOfRange = false;
9056 }
9057 return memOpnd;
9058 }
9059
FreeSpillRegMem(regno_t vrNum)9060 void AArch64CGFunc::FreeSpillRegMem(regno_t vrNum)
9061 {
9062 MemOperand *memOpnd = nullptr;
9063
9064 auto p = spillRegMemOperands.find(vrNum);
9065 if (p != spillRegMemOperands.end()) {
9066 memOpnd = p->second;
9067 }
9068
9069 if ((memOpnd == nullptr) && IsVRegNOForPseudoRegister(vrNum)) {
9070 auto pSecond = pRegSpillMemOperands.find(GetPseudoRegIdxFromVirtualRegNO(vrNum));
9071 if (pSecond != pRegSpillMemOperands.end()) {
9072 memOpnd = pSecond->second;
9073 }
9074 }
9075
9076 if (memOpnd == nullptr) {
9077 DEBUG_ASSERT(false, "free spillreg have no mem");
9078 return;
9079 }
9080
9081 uint32 size = memOpnd->GetSize();
9082 MapleUnorderedMap<uint32, SpillMemOperandSet *>::iterator iter;
9083 if ((iter = reuseSpillLocMem.find(size)) != reuseSpillLocMem.end()) {
9084 iter->second->Add(*memOpnd);
9085 } else {
9086 reuseSpillLocMem[size] = memPool->New<SpillMemOperandSet>(*GetFuncScopeAllocator());
9087 reuseSpillLocMem[size]->Add(*memOpnd);
9088 }
9089 }
9090
GetOrCreatSpillMem(regno_t vrNum,uint32 memSize)9091 MemOperand *AArch64CGFunc::GetOrCreatSpillMem(regno_t vrNum, uint32 memSize)
9092 {
9093 /* NOTES: must used in RA, not used in other place. */
9094 if (IsVRegNOForPseudoRegister(vrNum)) {
9095 auto p = pRegSpillMemOperands.find(GetPseudoRegIdxFromVirtualRegNO(vrNum));
9096 if (p != pRegSpillMemOperands.end()) {
9097 return p->second;
9098 }
9099 }
9100
9101 auto p = spillRegMemOperands.find(vrNum);
9102 if (p == spillRegMemOperands.end()) {
9103 if (vrNum >= vReg.VRegTableSize()) {
9104 CHECK_FATAL(false, "index out of range in AArch64CGFunc::FreeSpillRegMem");
9105 }
9106 uint32 memBitSize = (memSize <= k32BitSize) ? k32BitSize : (memSize <= k64BitSize) ? k64BitSize : k128BitSize;
9107 auto it = reuseSpillLocMem.find(memBitSize);
9108 if (it != reuseSpillLocMem.end()) {
9109 MemOperand *memOpnd = it->second->GetOne();
9110 if (memOpnd != nullptr) {
9111 (void)spillRegMemOperands.emplace(std::pair<regno_t, MemOperand *>(vrNum, memOpnd));
9112 return memOpnd;
9113 }
9114 }
9115
9116 RegOperand &baseOpnd = GetOrCreateStackBaseRegOperand();
9117 int64 offset = GetOrCreatSpillRegLocation(vrNum, memBitSize / kBitsPerByte);
9118 MemOperand *memOpnd = nullptr;
9119 OfstOperand *offsetOpnd = &CreateOfstOpnd(static_cast<uint64>(offset), k64BitSize);
9120 memOpnd = CreateMemOperand(MemOperand::kAddrModeBOi, memBitSize, baseOpnd, nullptr, offsetOpnd, nullptr);
9121 (void)spillRegMemOperands.emplace(std::pair<regno_t, MemOperand *>(vrNum, memOpnd));
9122 return memOpnd;
9123 } else {
9124 return p->second;
9125 }
9126 }
9127
GetPseudoRegisterSpillMemoryOperand(PregIdx i)9128 MemOperand *AArch64CGFunc::GetPseudoRegisterSpillMemoryOperand(PregIdx i)
9129 {
9130 MapleUnorderedMap<PregIdx, MemOperand *>::iterator p;
9131 if (GetCG()->GetOptimizeLevel() == CGOptions::kLevel0) {
9132 p = pRegSpillMemOperands.end();
9133 } else {
9134 p = pRegSpillMemOperands.find(i);
9135 }
9136 if (p != pRegSpillMemOperands.end()) {
9137 return p->second;
9138 }
9139 int64 offset = GetPseudoRegisterSpillLocation(i);
9140 MIRPreg *preg = GetFunction().GetPregTab()->PregFromPregIdx(i);
9141 uint32 bitLen = GetPrimTypeSize(preg->GetPrimType()) * kBitsPerByte;
9142 RegOperand &base = GetOrCreateFramePointerRegOperand();
9143
9144 OfstOperand &ofstOpnd = GetOrCreateOfstOpnd(static_cast<uint64>(offset), k32BitSize);
9145 MemOperand &memOpnd = GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, bitLen, &base, nullptr, &ofstOpnd, nullptr);
9146 if (IsImmediateOffsetOutOfRange(memOpnd, bitLen)) {
9147 MemOperand &newMemOpnd = SplitOffsetWithAddInstruction(memOpnd, bitLen);
9148 (void)pRegSpillMemOperands.emplace(std::pair<PregIdx, MemOperand *>(i, &newMemOpnd));
9149 return &newMemOpnd;
9150 }
9151 (void)pRegSpillMemOperands.emplace(std::pair<PregIdx, MemOperand *>(i, &memOpnd));
9152 return &memOpnd;
9153 }
9154
GetPseudoRegFromVirtualRegNO(const regno_t vRegNO,bool afterSSA) const9155 MIRPreg *AArch64CGFunc::GetPseudoRegFromVirtualRegNO(const regno_t vRegNO, bool afterSSA) const
9156 {
9157 PregIdx pri = afterSSA ? VRegNOToPRegIdx(vRegNO) : GetPseudoRegIdxFromVirtualRegNO(vRegNO);
9158 if (pri == -1)
9159 return nullptr;
9160 return GetFunction().GetPregTab()->PregFromPregIdx(pri);
9161 }
9162
9163 /* Get the number of return register of current function. */
GetReturnRegisterNumber()9164 AArch64reg AArch64CGFunc::GetReturnRegisterNumber()
9165 {
9166 CCImpl &retLocator = *GetOrCreateLocator(GetCurCallConvKind());
9167 CCLocInfo retMech;
9168 retLocator.LocateRetVal(*(GetFunction().GetReturnType()), retMech);
9169 if (retMech.GetRegCount() > 0) {
9170 return static_cast<AArch64reg>(retMech.GetReg0());
9171 }
9172 return kRinvalid;
9173 }
9174
CanLazyBinding(const Insn & ldrInsn) const9175 bool AArch64CGFunc::CanLazyBinding(const Insn &ldrInsn) const
9176 {
9177 Operand &memOpnd = ldrInsn.GetOperand(1);
9178 auto &aarchMemOpnd = static_cast<MemOperand &>(memOpnd);
9179 if (aarchMemOpnd.GetAddrMode() != MemOperand::kAddrModeLo12Li) {
9180 return false;
9181 }
9182
9183 const MIRSymbol *sym = aarchMemOpnd.GetSymbol();
9184 CHECK_FATAL(sym != nullptr, "sym can't be nullptr");
9185 if (sym->IsMuidFuncDefTab() || sym->IsMuidFuncUndefTab() || sym->IsMuidDataDefTab() || sym->IsMuidDataUndefTab() ||
9186 (sym->IsReflectionClassInfo() && !sym->IsReflectionArrayClassInfo())) {
9187 return true;
9188 }
9189
9190 return false;
9191 }
9192
9193 /*
9194 * add reg, reg, __PTR_C_STR_...
9195 * ldr reg1, [reg]
9196 * =>
9197 * ldr reg1, [reg, #:lo12:__Ptr_C_STR_...]
9198 */
ConvertAdrpl12LdrToLdr()9199 void AArch64CGFunc::ConvertAdrpl12LdrToLdr()
9200 {
9201 FOR_ALL_BB(bb, this)
9202 {
9203 FOR_BB_INSNS_SAFE(insn, bb, nextInsn)
9204 {
9205 nextInsn = insn->GetNextMachineInsn();
9206 if (nextInsn == nullptr) {
9207 break;
9208 }
9209 if (!insn->IsMachineInstruction()) {
9210 continue;
9211 }
9212 /* check first insn */
9213 MOperator thisMop = insn->GetMachineOpcode();
9214 if (thisMop != MOP_xadrpl12) {
9215 continue;
9216 }
9217 /* check second insn */
9218 MOperator nextMop = nextInsn->GetMachineOpcode();
9219 if (!(((nextMop >= MOP_wldrsb) && (nextMop <= MOP_dldp)) ||
9220 ((nextMop >= MOP_wstrb) && (nextMop <= MOP_dstp)))) {
9221 continue;
9222 }
9223
9224 /* Check if base register of nextInsn and the dest operand of insn are identical. */
9225 MemOperand *memOpnd = static_cast<MemOperand *>(nextInsn->GetMemOpnd());
9226 CHECK_FATAL(memOpnd != nullptr, "memOpnd can't be nullptr");
9227
9228 /* Only for AddrMode_B_OI addressing mode. */
9229 if (memOpnd->GetAddrMode() != MemOperand::kAddrModeBOi) {
9230 continue;
9231 }
9232
9233 /* Only for intact memory addressing. */
9234 if (!memOpnd->IsIntactIndexed()) {
9235 continue;
9236 }
9237
9238 auto ®Opnd = static_cast<RegOperand &>(insn->GetOperand(0));
9239
9240 /* Check if dest operand of insn is idential with base register of nextInsn. */
9241 RegOperand *baseReg = memOpnd->GetBaseRegister();
9242 CHECK_FATAL(baseReg != nullptr, "baseReg can't be nullptr");
9243 if (baseReg->GetRegisterNumber() != regOpnd.GetRegisterNumber()) {
9244 continue;
9245 }
9246
9247 StImmOperand &stImmOpnd = static_cast<StImmOperand &>(insn->GetOperand(kInsnThirdOpnd));
9248 OfstOperand &ofstOpnd = GetOrCreateOfstOpnd(
9249 static_cast<uint64>(stImmOpnd.GetOffset() + memOpnd->GetOffsetImmediate()->GetOffsetValue()),
9250 k32BitSize);
9251 RegOperand &newBaseOpnd = static_cast<RegOperand &>(insn->GetOperand(kInsnSecondOpnd));
9252 MemOperand &newMemOpnd = GetOrCreateMemOpnd(MemOperand::kAddrModeLo12Li, memOpnd->GetSize(), &newBaseOpnd,
9253 nullptr, &ofstOpnd, stImmOpnd.GetSymbol());
9254 nextInsn->SetOperand(1, newMemOpnd);
9255 bb->RemoveInsn(*insn);
9256 }
9257 }
9258 }
9259
9260 /*
9261 * adrp reg1, __muid_func_undef_tab..
9262 * ldr reg2, [reg1, #:lo12:__muid_func_undef_tab..]
9263 * =>
9264 * intrinsic_adrp_ldr reg2, __muid_func_undef_tab...
9265 */
ConvertAdrpLdrToIntrisic()9266 void AArch64CGFunc::ConvertAdrpLdrToIntrisic()
9267 {
9268 FOR_ALL_BB(bb, this)
9269 {
9270 FOR_BB_INSNS_SAFE(insn, bb, nextInsn)
9271 {
9272 nextInsn = insn->GetNextMachineInsn();
9273 if (nextInsn == nullptr) {
9274 break;
9275 }
9276 if (!insn->IsMachineInstruction()) {
9277 continue;
9278 }
9279
9280 MOperator firstMop = insn->GetMachineOpcode();
9281 MOperator secondMop = nextInsn->GetMachineOpcode();
9282 if (!((firstMop == MOP_xadrp) && ((secondMop == MOP_wldr) || (secondMop == MOP_xldr)))) {
9283 continue;
9284 }
9285
9286 if (CanLazyBinding(*nextInsn)) {
9287 bb->ReplaceInsn(
9288 *insn, GetInsnBuilder()->BuildInsn(MOP_adrp_ldr, nextInsn->GetOperand(0), insn->GetOperand(1)));
9289 bb->RemoveInsn(*nextInsn);
9290 }
9291 }
9292 }
9293 }
9294
ProcessLazyBinding()9295 void AArch64CGFunc::ProcessLazyBinding()
9296 {
9297 ConvertAdrpl12LdrToLdr();
9298 ConvertAdrpLdrToIntrisic();
9299 }
9300
9301 /*
9302 * Generate global long call
9303 * adrp VRx, symbol
9304 * ldr VRx, [VRx, #:lo12:symbol]
9305 * blr VRx
9306 *
9307 * Input:
9308 * insn : insert new instruction after the 'insn'
9309 * func : the symbol of the function need to be called
9310 * srcOpnds : list operand of the function need to be called
9311 * isCleanCall: when generate clean call insn, set isCleanCall as true
9312 * Return: the 'blr' instruction
9313 */
GenerateGlobalLongCallAfterInsn(const MIRSymbol & func,ListOperand & srcOpnds)9314 Insn &AArch64CGFunc::GenerateGlobalLongCallAfterInsn(const MIRSymbol &func, ListOperand &srcOpnds)
9315 {
9316 MIRSymbol *symbol = GetFunction().GetLocalOrGlobalSymbol(func.GetStIdx());
9317 symbol->SetStorageClass(kScGlobal);
9318 RegOperand &tmpReg = CreateRegisterOperandOfType(PTY_u64);
9319 StImmOperand &stOpnd = CreateStImmOperand(*symbol, 0, 0);
9320 OfstOperand &offsetOpnd = CreateOfstOpnd(*symbol, 0);
9321 Insn &adrpInsn = GetInsnBuilder()->BuildInsn(MOP_xadrp, tmpReg, stOpnd);
9322 GetCurBB()->AppendInsn(adrpInsn);
9323 MemOperand &memOrd = GetOrCreateMemOpnd(MemOperand::kAddrModeLo12Li, GetPointerSize() * kBitsPerByte,
9324 static_cast<RegOperand *>(&tmpReg), nullptr, &offsetOpnd, symbol);
9325 Insn &ldrInsn = GetInsnBuilder()->BuildInsn(memOrd.GetSize() == k64BitSize ? MOP_xldr : MOP_wldr, tmpReg, memOrd);
9326 GetCurBB()->AppendInsn(ldrInsn);
9327
9328 Insn &callInsn = GetInsnBuilder()->BuildInsn(MOP_xblr, tmpReg, srcOpnds);
9329 GetCurBB()->AppendInsn(callInsn);
9330 GetCurBB()->SetHasCall();
9331 return callInsn;
9332 }
9333
9334 /*
9335 * Generate local long call
9336 * adrp VRx, symbol
9337 * add VRx, VRx, #:lo12:symbol
9338 * blr VRx
9339 *
9340 * Input:
9341 * insn : insert new instruction after the 'insn'
9342 * func : the symbol of the function need to be called
9343 * srcOpnds : list operand of the function need to be called
9344 * isCleanCall: when generate clean call insn, set isCleanCall as true
9345 * Return: the 'blr' instruction
9346 */
GenerateLocalLongCallAfterInsn(const MIRSymbol & func,ListOperand & srcOpnds)9347 Insn &AArch64CGFunc::GenerateLocalLongCallAfterInsn(const MIRSymbol &func, ListOperand &srcOpnds)
9348 {
9349 RegOperand &tmpReg = CreateRegisterOperandOfType(PTY_u64);
9350 StImmOperand &stOpnd = CreateStImmOperand(func, 0, 0);
9351 Insn &adrpInsn = GetInsnBuilder()->BuildInsn(MOP_xadrp, tmpReg, stOpnd);
9352 GetCurBB()->AppendInsn(adrpInsn);
9353 Insn &addInsn = GetInsnBuilder()->BuildInsn(MOP_xadrpl12, tmpReg, tmpReg, stOpnd);
9354 GetCurBB()->AppendInsn(addInsn);
9355 Insn *callInsn = &GetInsnBuilder()->BuildInsn(MOP_xblr, tmpReg, srcOpnds);
9356 GetCurBB()->AppendInsn(*callInsn);
9357 GetCurBB()->SetHasCall();
9358 return *callInsn;
9359 }
9360
AppendCall(const MIRSymbol & sym,ListOperand & srcOpnds)9361 Insn &AArch64CGFunc::AppendCall(const MIRSymbol &sym, ListOperand &srcOpnds)
9362 {
9363 Insn *callInsn = nullptr;
9364 if (CGOptions::IsLongCalls()) {
9365 MIRFunction *mirFunc = sym.GetFunction();
9366 if (IsDuplicateAsmList(sym) || (mirFunc && mirFunc->GetAttr(FUNCATTR_local))) {
9367 callInsn = &GenerateLocalLongCallAfterInsn(sym, srcOpnds);
9368 } else {
9369 callInsn = &GenerateGlobalLongCallAfterInsn(sym, srcOpnds);
9370 }
9371 } else {
9372 Operand &targetOpnd = GetOrCreateFuncNameOpnd(sym);
9373 callInsn = &GetInsnBuilder()->BuildInsn(MOP_xbl, targetOpnd, srcOpnds);
9374 GetCurBB()->AppendInsn(*callInsn);
9375 GetCurBB()->SetHasCall();
9376 }
9377 return *callInsn;
9378 }
9379
IsDuplicateAsmList(const MIRSymbol & sym) const9380 bool AArch64CGFunc::IsDuplicateAsmList(const MIRSymbol &sym) const
9381 {
9382 if (CGOptions::IsDuplicateAsmFileEmpty()) {
9383 return false;
9384 }
9385
9386 const std::string &name = sym.GetName();
9387 if ((name == "strlen") || (name == "strncmp") || (name == "memcpy") || (name == "memmove") || (name == "strcmp") ||
9388 (name == "memcmp") || (name == "memcmpMpl")) {
9389 return true;
9390 }
9391 return false;
9392 }
9393
SelectMPLProfCounterInc(const IntrinsiccallNode & intrnNode)9394 void AArch64CGFunc::SelectMPLProfCounterInc(const IntrinsiccallNode &intrnNode)
9395 {
9396 if (Options::profileGen) {
9397 DEBUG_ASSERT(intrnNode.NumOpnds() == 1, "must be 1 operand");
9398 BaseNode *arg1 = intrnNode.Opnd(0);
9399 DEBUG_ASSERT(arg1 != nullptr, "nullptr check");
9400 regno_t vRegNO1 = NewVReg(GetRegTyFromPrimTy(PTY_a64), GetPrimTypeSize(PTY_a64));
9401 RegOperand &vReg1 = CreateVirtualRegisterOperand(vRegNO1);
9402 vReg1.SetRegNotBBLocal();
9403 static const MIRSymbol *bbProfileTab = nullptr;
9404
9405 // Ref: MeProfGen::InstrumentFunc on ctrTbl namiLogicalShiftLeftOperandng
9406 std::string ctrTblName = namemangler::kprefixProfCtrTbl + GetMirModule().GetFileName() + "_" + GetName();
9407 std::replace(ctrTblName.begin(), ctrTblName.end(), '.', '_');
9408 std::replace(ctrTblName.begin(), ctrTblName.end(), '-', '_');
9409 std::replace(ctrTblName.begin(), ctrTblName.end(), '/', '_');
9410
9411 if (!bbProfileTab || bbProfileTab->GetName() != ctrTblName) {
9412 bbProfileTab = GetMirModule().GetMIRBuilder()->GetGlobalDecl(ctrTblName);
9413 CHECK_FATAL(bbProfileTab != nullptr, "expect counter table");
9414 }
9415
9416 ConstvalNode *constvalNode = static_cast<ConstvalNode *>(arg1);
9417 MIRConst *mirConst = constvalNode->GetConstVal();
9418 DEBUG_ASSERT(mirConst != nullptr, "nullptr check");
9419 CHECK_FATAL(mirConst->GetKind() == kConstInt, "expect MIRIntConst type");
9420 MIRIntConst *mirIntConst = safe_cast<MIRIntConst>(mirConst);
9421 int64 offset = static_cast<int64>(GetPrimTypeSize(PTY_u64)) * mirIntConst->GetExtValue();
9422
9423 if (!CGOptions::IsQuiet()) {
9424 maple::LogInfo::MapleLogger(kLlInfo) << "At counter table offset: " << offset << std::endl;
9425 }
9426 MemOperand *memOpnd = &GetOrCreateMemOpnd(*bbProfileTab, offset, k64BitSize);
9427 if (IsImmediateOffsetOutOfRange(*memOpnd, k64BitSize)) {
9428 memOpnd = &SplitOffsetWithAddInstruction(*memOpnd, k64BitSize);
9429 }
9430 Operand *reg = &SelectCopy(*memOpnd, PTY_u64, PTY_u64);
9431 ImmOperand &one = CreateImmOperand(1, k64BitSize, false);
9432 SelectAdd(*reg, *reg, one, PTY_u64);
9433 SelectCopy(*memOpnd, PTY_u64, *reg, PTY_u64);
9434 return;
9435 }
9436
9437 DEBUG_ASSERT(intrnNode.NumOpnds() == 1, "must be 1 operand");
9438 BaseNode *arg1 = intrnNode.Opnd(0);
9439 DEBUG_ASSERT(arg1 != nullptr, "nullptr check");
9440 regno_t vRegNO1 = NewVReg(GetRegTyFromPrimTy(PTY_a64), GetPrimTypeSize(PTY_a64));
9441 RegOperand &vReg1 = CreateVirtualRegisterOperand(vRegNO1);
9442 vReg1.SetRegNotBBLocal();
9443 static const MIRSymbol *bbProfileTab = nullptr;
9444 if (!bbProfileTab) {
9445 std::string bbProfileName = namemangler::kBBProfileTabPrefixStr + GetMirModule().GetFileNameAsPostfix();
9446 bbProfileTab = GetMirModule().GetMIRBuilder()->GetGlobalDecl(bbProfileName);
9447 CHECK_FATAL(bbProfileTab != nullptr, "expect bb profile tab");
9448 }
9449 ConstvalNode *constvalNode = static_cast<ConstvalNode *>(arg1);
9450 MIRConst *mirConst = constvalNode->GetConstVal();
9451 DEBUG_ASSERT(mirConst != nullptr, "nullptr check");
9452 CHECK_FATAL(mirConst->GetKind() == kConstInt, "expect MIRIntConst type");
9453 MIRIntConst *mirIntConst = safe_cast<MIRIntConst>(mirConst);
9454 int64 idx = static_cast<int64>(GetPrimTypeSize(PTY_u32)) * mirIntConst->GetExtValue();
9455 if (!CGOptions::IsQuiet()) {
9456 maple::LogInfo::MapleLogger(kLlErr) << "Id index " << idx << std::endl;
9457 }
9458 StImmOperand &stOpnd = CreateStImmOperand(*bbProfileTab, idx, 0);
9459 Insn &newInsn = GetInsnBuilder()->BuildInsn(MOP_counter, vReg1, stOpnd);
9460 newInsn.SetDoNotRemove(true);
9461 GetCurBB()->AppendInsn(newInsn);
9462 }
9463
SelectMPLClinitCheck(const IntrinsiccallNode & intrnNode)9464 void AArch64CGFunc::SelectMPLClinitCheck(const IntrinsiccallNode &intrnNode)
9465 {
9466 DEBUG_ASSERT(intrnNode.NumOpnds() == 1, "must be 1 operand");
9467 BaseNode *arg = intrnNode.Opnd(0);
9468 Operand *stOpnd = nullptr;
9469 bool bClinitSeperate = false;
9470 DEBUG_ASSERT(CGOptions::IsPIC(), "must be doPIC");
9471 if (arg->GetOpCode() == OP_addrof) {
9472 AddrofNode *addrof = static_cast<AddrofNode *>(arg);
9473 MIRSymbol *symbol = GetFunction().GetLocalOrGlobalSymbol(addrof->GetStIdx());
9474 DEBUG_ASSERT(symbol->GetName().find(CLASSINFO_PREFIX_STR) == 0, "must be a symbol with __classinfo__");
9475
9476 if (!symbol->IsMuidDataUndefTab()) {
9477 std::string ptrName = namemangler::kPtrPrefixStr + symbol->GetName();
9478 MIRType *ptrType = GlobalTables::GetTypeTable().GetPtr();
9479 symbol = GetMirModule().GetMIRBuilder()->GetOrCreateGlobalDecl(ptrName, *ptrType);
9480 bClinitSeperate = true;
9481 symbol->SetStorageClass(kScFstatic);
9482 }
9483 stOpnd = &CreateStImmOperand(*symbol, 0, 0);
9484 } else {
9485 arg = arg->Opnd(0);
9486 BaseNode *arg0 = arg->Opnd(0);
9487 BaseNode *arg1 = arg->Opnd(1);
9488 DEBUG_ASSERT(arg0 != nullptr, "nullptr check");
9489 DEBUG_ASSERT(arg1 != nullptr, "nullptr check");
9490 DEBUG_ASSERT(arg0->GetOpCode() == OP_addrof, "expect the operand to be addrof");
9491 AddrofNode *addrof = static_cast<AddrofNode *>(arg0);
9492 MIRSymbol *symbol = GetFunction().GetLocalOrGlobalSymbol(addrof->GetStIdx());
9493 DEBUG_ASSERT(addrof->GetFieldID() == 0, "For debug SelectMPLClinitCheck.");
9494 ConstvalNode *constvalNode = static_cast<ConstvalNode *>(arg1);
9495 MIRConst *mirConst = constvalNode->GetConstVal();
9496 DEBUG_ASSERT(mirConst != nullptr, "nullptr check");
9497 CHECK_FATAL(mirConst->GetKind() == kConstInt, "expect MIRIntConst type");
9498 MIRIntConst *mirIntConst = safe_cast<MIRIntConst>(mirConst);
9499 stOpnd = &CreateStImmOperand(*symbol, mirIntConst->GetExtValue(), 0);
9500 }
9501
9502 regno_t vRegNO2 = NewVReg(GetRegTyFromPrimTy(PTY_a64), GetPrimTypeSize(PTY_a64));
9503 RegOperand &vReg2 = CreateVirtualRegisterOperand(vRegNO2);
9504 vReg2.SetRegNotBBLocal();
9505 if (bClinitSeperate) {
9506 /* Seperate MOP_clinit to MOP_adrp_ldr + MOP_clinit_tail. */
9507 Insn &newInsn = GetInsnBuilder()->BuildInsn(MOP_adrp_ldr, vReg2, *stOpnd);
9508 GetCurBB()->AppendInsn(newInsn);
9509 newInsn.SetDoNotRemove(true);
9510 Insn &insn = GetInsnBuilder()->BuildInsn(MOP_clinit_tail, vReg2);
9511 insn.SetDoNotRemove(true);
9512 GetCurBB()->AppendInsn(insn);
9513 } else {
9514 Insn &newInsn = GetInsnBuilder()->BuildInsn(MOP_clinit, vReg2, *stOpnd);
9515 GetCurBB()->AppendInsn(newInsn);
9516 }
9517 }
GenCVaStartIntrin(RegOperand & opnd,uint32 stkSize)9518 void AArch64CGFunc::GenCVaStartIntrin(RegOperand &opnd, uint32 stkSize)
9519 {
9520 /* FPLR only pushed in regalloc() after intrin function */
9521 Operand &stkOpnd = GetOrCreatePhysicalRegisterOperand(RFP, k64BitSize, kRegTyInt);
9522
9523 /* __stack */
9524 ImmOperand *offsOpnd;
9525 int64 coldToStk = static_cast<int64>(static_cast<AArch64MemLayout *>(GetMemlayout())->GetSizeOfColdToStk());
9526 if (GetMirModule().GetFlavor() != MIRFlavor::kFlavorLmbc) {
9527 // unvary reset StackFrameSize
9528 // unvary to stk bot = coldToStk
9529 offsOpnd = &CreateImmOperand(coldToStk, k64BitSize, true, kUnAdjustVary); /* isvary reset StackFrameSize */
9530 } else {
9531 offsOpnd = &CreateImmOperand(0, k64BitSize, true);
9532 }
9533 ImmOperand *offsOpnd2 = &CreateImmOperand(stkSize, k64BitSize, false);
9534 RegOperand &vReg = CreateVirtualRegisterOperand(NewVReg(kRegTyInt, GetPrimTypeSize(GetLoweredPtrType())));
9535 if (stkSize) {
9536 SelectAdd(vReg, *offsOpnd, *offsOpnd2, GetLoweredPtrType());
9537 SelectAdd(vReg, stkOpnd, vReg, GetLoweredPtrType());
9538 } else {
9539 SelectAdd(vReg, stkOpnd, *offsOpnd, GetLoweredPtrType()); /* stack pointer */
9540 }
9541 OfstOperand *offOpnd = &GetOrCreateOfstOpnd(0, k64BitSize); /* va_list ptr */
9542 /* mem operand in va_list struct (lhs) */
9543 MemOperand *strOpnd = &GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, k64BitSize, &opnd, nullptr, offOpnd,
9544 static_cast<MIRSymbol *>(nullptr));
9545 GetCurBB()->AppendInsn(
9546 GetInsnBuilder()->BuildInsn(vReg.GetSize() == k64BitSize ? MOP_xstr : MOP_wstr, vReg, *strOpnd));
9547
9548 /* __gr_top ; it's the same as __stack before the 1st va_arg */
9549 if (CGOptions::IsArm64ilp32()) {
9550 offOpnd = &GetOrCreateOfstOpnd(GetPointerSize(), k64BitSize);
9551 } else {
9552 offOpnd = &GetOrCreateOfstOpnd(k8BitSize, k64BitSize);
9553 }
9554 strOpnd = &GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, k64BitSize, &opnd, nullptr, offOpnd,
9555 static_cast<MIRSymbol *>(nullptr));
9556 SelectAdd(vReg, stkOpnd, *offsOpnd, GetLoweredPtrType());
9557 GetCurBB()->AppendInsn(
9558 GetInsnBuilder()->BuildInsn(vReg.GetSize() == k64BitSize ? MOP_xstr : MOP_wstr, vReg, *strOpnd));
9559
9560 /* __vr_top */
9561 int32 grAreaSize = static_cast<int32>(static_cast<AArch64MemLayout *>(GetMemlayout())->GetSizeOfGRSaveArea());
9562 if (CGOptions::IsArm64ilp32()) {
9563 offsOpnd2 = &CreateImmOperand(static_cast<int64>(RoundUp(static_cast<uint64>(grAreaSize), k8ByteSize << 1)),
9564 k64BitSize, false);
9565 } else {
9566 offsOpnd2 = &CreateImmOperand(
9567 static_cast<int64>(RoundUp(static_cast<uint64>(grAreaSize), GetPointerSize() << 1)), k64BitSize, false);
9568 }
9569 SelectSub(vReg, *offsOpnd, *offsOpnd2, GetLoweredPtrType()); /* if 1st opnd is register => sub */
9570 SelectAdd(vReg, stkOpnd, vReg, GetLoweredPtrType());
9571 offOpnd = &GetOrCreateOfstOpnd(GetPointerSize() << 1, k64BitSize);
9572 strOpnd = &GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, k64BitSize, &opnd, nullptr, offOpnd,
9573 static_cast<MIRSymbol *>(nullptr));
9574 GetCurBB()->AppendInsn(
9575 GetInsnBuilder()->BuildInsn(vReg.GetSize() == k64BitSize ? MOP_xstr : MOP_wstr, vReg, *strOpnd));
9576
9577 /* __gr_offs */
9578 int32 offs = 0 - grAreaSize;
9579 offsOpnd = &CreateImmOperand(offs, k32BitSize, false);
9580 RegOperand *tmpReg = &CreateRegisterOperandOfType(PTY_i32); /* offs value to be assigned (rhs) */
9581 SelectCopyImm(*tmpReg, *offsOpnd, PTY_i32);
9582 // write __gr_offs : offset from gr_top to next GP register arg
9583 // accroding to typedef struct va_list
9584 // the field offset of __gr_offs is 3 pointer from beginning
9585 offOpnd = &GetOrCreateOfstOpnd(GetPointerSize() * 3, k32BitSize);
9586 strOpnd = &GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, k32BitSize, &opnd, nullptr, offOpnd,
9587 static_cast<MIRSymbol *>(nullptr));
9588 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wstr, *tmpReg, *strOpnd));
9589
9590 /* __vr_offs */
9591 offs =
9592 static_cast<int32>(UINT32_MAX - (static_cast<AArch64MemLayout *>(GetMemlayout())->GetSizeOfVRSaveArea() - 1UL));
9593 offsOpnd = &CreateImmOperand(offs, k32BitSize, false);
9594 tmpReg = &CreateRegisterOperandOfType(PTY_i32);
9595 SelectCopyImm(*tmpReg, *offsOpnd, PTY_i32);
9596 // write __vr_offs : offset from vr_top to next FP/SIMD register arg
9597 // accroding to typedef struct va_list
9598 // the field offset of __vr_offs is 3 pointer + sizeof(int) from beginning
9599 offOpnd = &GetOrCreateOfstOpnd((GetPointerSize() * 3 + sizeof(int32)), k32BitSize);
9600 strOpnd = &GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, k32BitSize, &opnd, nullptr, offOpnd,
9601 static_cast<MIRSymbol *>(nullptr));
9602 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wstr, *tmpReg, *strOpnd));
9603 }
9604
SelectCVaStart(const IntrinsiccallNode & intrnNode)9605 void AArch64CGFunc::SelectCVaStart(const IntrinsiccallNode &intrnNode)
9606 {
9607 DEBUG_ASSERT(intrnNode.NumOpnds() == 2, "must be 2 operands"); // must be 2 operands
9608 /* 2 operands, but only 1 needed. Don't need to emit code for second operand
9609 *
9610 * va_list is a passed struct with an address, load its address
9611 */
9612 isIntrnCallForC = true;
9613 BaseNode *argExpr = intrnNode.Opnd(0);
9614 Operand *opnd = HandleExpr(intrnNode, *argExpr);
9615 RegOperand &opnd0 = LoadIntoRegister(*opnd, GetLoweredPtrType()); /* first argument of intrinsic */
9616
9617 /* Find beginning of unnamed arg on stack.
9618 * Ex. void foo(int i1, int i2, ... int i8, struct S r, struct S s, ...)
9619 * where struct S has size 32, address of r and s are on stack but they are named.
9620 */
9621 AArch64CallConvImpl parmLocator(GetBecommon());
9622 CCLocInfo pLoc;
9623 uint32 stkSize = 0;
9624 uint32 inReg = 0;
9625 for (uint32 i = 0; i < GetFunction().GetFormalCount(); i++) {
9626 MIRType *ty = GlobalTables::GetTypeTable().GetTypeFromTyIdx(GetFunction().GetNthParamTyIdx(i));
9627 parmLocator.LocateNextParm(*ty, pLoc);
9628 if (pLoc.reg0 == kRinvalid) { /* on stack */
9629 stkSize = static_cast<uint32_t>(pLoc.memOffset + pLoc.memSize);
9630 } else {
9631 inReg++;
9632 }
9633 }
9634 if (GetMirModule().GetFlavor() == MIRFlavor::kFlavorLmbc) {
9635 stkSize += (inReg * k8ByteSize);
9636 }
9637 if (CGOptions::IsArm64ilp32()) {
9638 stkSize = static_cast<uint32>(RoundUp(stkSize, k8ByteSize));
9639 } else {
9640 stkSize = static_cast<uint32>(RoundUp(stkSize, GetPointerSize()));
9641 }
9642
9643 GenCVaStartIntrin(opnd0, stkSize);
9644
9645 return;
9646 }
9647
9648 // output
9649 // add_with_overflow/ sub_with_overflow:
9650 // w1: parm1
9651 // w2: parm2
9652 // adds/subs w0, w1, w2
9653 // cset w3, vs
9654
9655 // mul_with_overflow:
9656 // w1: parm1
9657 // w2: parm2
9658 // smull x0, w0, w1
9659 // cmp x0, w0, sxtw
9660 // cset w4, ne
SelectOverFlowCall(const IntrinsiccallNode & intrnNode)9661 void AArch64CGFunc::SelectOverFlowCall(const IntrinsiccallNode &intrnNode)
9662 {
9663 DEBUG_ASSERT(intrnNode.NumOpnds() == 2, "must be 2 operands"); // must be 2 operands
9664 MIRIntrinsicID intrinsic = intrnNode.GetIntrinsic();
9665 PrimType type = intrnNode.Opnd(0)->GetPrimType();
9666 PrimType type2 = intrnNode.Opnd(1)->GetPrimType();
9667 CHECK_FATAL(type == PTY_i32 || type == PTY_u32, "only support i32 or u32 here");
9668 CHECK_FATAL(type2 == PTY_i32 || type2 == PTY_u32, "only support i32 or u32 here");
9669 // deal with parms
9670 RegOperand &opnd0 = LoadIntoRegister(*HandleExpr(intrnNode, *intrnNode.Opnd(0)),
9671 intrnNode.Opnd(0)->GetPrimType()); /* first argument of intrinsic */
9672 RegOperand &opnd1 = LoadIntoRegister(*HandleExpr(intrnNode, *intrnNode.Opnd(1)),
9673 intrnNode.Opnd(1)->GetPrimType()); /* first argument of intrinsic */
9674 RegOperand &resReg = CreateRegisterOperandOfType(type);
9675 RegOperand &resReg2 = CreateRegisterOperandOfType(PTY_u8);
9676 Operand &rflag = GetOrCreateRflag();
9677 // arith operation with set flag
9678 if (intrinsic == INTRN_ADD_WITH_OVERFLOW) {
9679 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_waddsrrr, rflag, resReg, opnd0, opnd1));
9680 SelectAArch64CSet(resReg2, GetCondOperand(CC_VS), false);
9681 } else if (intrinsic == INTRN_SUB_WITH_OVERFLOW) {
9682 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wsubsrrr, rflag, resReg, opnd0, opnd1));
9683 SelectAArch64CSet(resReg2, GetCondOperand(CC_VS), false);
9684 } else if (intrinsic == INTRN_MUL_WITH_OVERFLOW) {
9685 // smull
9686 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xsmullrrr, resReg, opnd0, opnd1));
9687 Operand &sxtw = CreateExtendShiftOperand(ExtendShiftOperand::kSXTW, 0, k3BitSize);
9688 Insn &cmpInsn = GetInsnBuilder()->BuildInsn(MOP_xwcmprre, rflag, resReg, resReg, sxtw);
9689 GetCurBB()->AppendInsn(cmpInsn);
9690 SelectAArch64CSet(resReg2, GetCondOperand(CC_NE), false);
9691 } else {
9692 CHECK_FATAL(false, "niy");
9693 }
9694 // store back
9695 auto *retVals = &intrnNode.GetReturnVec();
9696 auto &pair = retVals->at(0);
9697 stIdx2OverflowResult[pair.first] = std::pair<RegOperand *, RegOperand *>(&resReg, &resReg2);
9698 return;
9699 }
9700
9701 /*
9702 * intrinsiccall C___Atomic_store_N(ptr, val, memorder))
9703 * ====> *ptr = val
9704 * let ptr -> x0
9705 * let val -> x1
9706 * implement to asm: str/stlr x1, [x0]
9707 * a store-release would replace str if memorder is not 0
9708 */
SelectCAtomicStoreN(const IntrinsiccallNode & intrinsiccallNode)9709 void AArch64CGFunc::SelectCAtomicStoreN(const IntrinsiccallNode &intrinsiccallNode)
9710 {
9711 auto primType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(intrinsiccallNode.GetTyIdx())->GetPrimType();
9712 auto *addr = HandleExpr(intrinsiccallNode, *intrinsiccallNode.Opnd(0));
9713 auto *value = HandleExpr(intrinsiccallNode, *intrinsiccallNode.Opnd(1));
9714 auto *memOrderOpnd = intrinsiccallNode.Opnd(kInsnThirdOpnd);
9715 std::memory_order memOrder = std::memory_order_seq_cst;
9716 if (memOrderOpnd->IsConstval()) {
9717 auto *memOrderConst = static_cast<MIRIntConst *>(static_cast<ConstvalNode *>(memOrderOpnd)->GetConstVal());
9718 memOrder = static_cast<std::memory_order>(memOrderConst->GetExtValue());
9719 }
9720 SelectAtomicStore(*value, *addr, primType, PickMemOrder(memOrder, false));
9721 }
9722
SelectAtomicStore(Operand & srcOpnd,Operand & addrOpnd,PrimType primType,AArch64isa::MemoryOrdering memOrder)9723 void AArch64CGFunc::SelectAtomicStore(Operand &srcOpnd, Operand &addrOpnd, PrimType primType,
9724 AArch64isa::MemoryOrdering memOrder)
9725 {
9726 auto &memOpnd = CreateMemOpnd(LoadIntoRegister(addrOpnd, PTY_a64), 0, k64BitSize);
9727 auto mOp = PickStInsn(GetPrimTypeBitSize(primType), primType, memOrder);
9728 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, LoadIntoRegister(srcOpnd, primType), memOpnd));
9729 }
9730
SelectAddrofThreadLocal(Operand & result,StImmOperand & stImm)9731 void AArch64CGFunc::SelectAddrofThreadLocal(Operand &result, StImmOperand &stImm)
9732 {
9733 if (CGOptions::IsPIC()) {
9734 SelectCTlsGlobalDesc(result, stImm);
9735 } else {
9736 SelectCTlsLocalDesc(result, stImm);
9737 }
9738 if (stImm.GetOffset() > 0) {
9739 auto &immOpnd = CreateImmOperand(stImm.GetOffset(), result.GetSize(), false);
9740 SelectAdd(result, result, immOpnd, PTY_u64);
9741 }
9742 }
9743
SelectCTlsLocalDesc(Operand & result,StImmOperand & stImm)9744 void AArch64CGFunc::SelectCTlsLocalDesc(Operand &result, StImmOperand &stImm)
9745 {
9746 auto tpidr = &CreateCommentOperand("tpidr_el0");
9747 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_mrs, result, *tpidr));
9748 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_tls_desc_rel, result, result, stImm));
9749 }
9750
SelectCTlsGlobalDesc(Operand & result,StImmOperand & stImm)9751 void AArch64CGFunc::SelectCTlsGlobalDesc(Operand &result, StImmOperand &stImm)
9752 {
9753 /* according to AArch64 Machine Directives */
9754 auto &r0opnd = GetOrCreatePhysicalRegisterOperand(R0, k64BitSize, GetRegTyFromPrimTy(PTY_u64));
9755 RegOperand *tlsAddr = &CreateRegisterOperandOfType(PTY_u64);
9756 RegOperand *specialFunc = &CreateRegisterOperandOfType(PTY_u64);
9757 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_tls_desc_call, r0opnd, *tlsAddr, stImm));
9758 /* release tls address */
9759 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_pseduo_tls_release, *tlsAddr));
9760 // mrs xn, tpidr_el0
9761 // add x0, x0, xn
9762 auto tpidr = &CreateCommentOperand("tpidr_el0");
9763 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_mrs, *specialFunc, *tpidr));
9764 SelectAdd(result, r0opnd, *specialFunc, PTY_u64);
9765 }
9766
SelectIntrinsicCall(IntrinsiccallNode & intrinsiccallNode)9767 void AArch64CGFunc::SelectIntrinsicCall(IntrinsiccallNode &intrinsiccallNode)
9768 {
9769 MIRIntrinsicID intrinsic = intrinsiccallNode.GetIntrinsic();
9770
9771 if (GetCG()->GenerateVerboseCG()) {
9772 std::string comment = GetIntrinsicName(intrinsic);
9773 GetCurBB()->AppendInsn(CreateCommentInsn(comment));
9774 }
9775
9776 /*
9777 * At this moment, we eagerly evaluates all argument expressions. In theory,
9778 * there could be intrinsics that extract meta-information of variables, such as
9779 * their locations, rather than computing their values. Applications
9780 * include building stack maps that help runtime libraries to find the values
9781 * of local variables (See @stackmap in LLVM), in which case knowing their
9782 * locations will suffice.
9783 */
9784 if (intrinsic == INTRN_MPL_PROF_COUNTER_INC) { /* special case */
9785 SelectMPLProfCounterInc(intrinsiccallNode);
9786 return;
9787 }
9788 // js
9789 if (intrinsic == INTRN_ADD_WITH_OVERFLOW || intrinsic == INTRN_SUB_WITH_OVERFLOW ||
9790 intrinsic == INTRN_MUL_WITH_OVERFLOW) {
9791 SelectOverFlowCall(intrinsiccallNode);
9792 return;
9793 }
9794 switch (intrinsic) {
9795 case INTRN_C_va_start:
9796 SelectCVaStart(intrinsiccallNode);
9797 return;
9798 case INTRN_C___sync_lock_release_1:
9799 SelectCSyncLockRelease(intrinsiccallNode, PTY_u8);
9800 return;
9801 case INTRN_C___sync_lock_release_2:
9802 SelectCSyncLockRelease(intrinsiccallNode, PTY_u16);
9803 return;
9804 case INTRN_C___sync_lock_release_4:
9805 SelectCSyncLockRelease(intrinsiccallNode, PTY_u32);
9806 return;
9807 case INTRN_C___sync_lock_release_8:
9808 SelectCSyncLockRelease(intrinsiccallNode, PTY_u64);
9809 return;
9810 case INTRN_C___atomic_store_n:
9811 SelectCAtomicStoreN(intrinsiccallNode);
9812 return;
9813 case INTRN_vector_zip_v8u8:
9814 case INTRN_vector_zip_v8i8:
9815 case INTRN_vector_zip_v4u16:
9816 case INTRN_vector_zip_v4i16:
9817 case INTRN_vector_zip_v2u32:
9818 case INTRN_vector_zip_v2i32:
9819 SelectVectorZip(intrinsiccallNode.Opnd(0)->GetPrimType(),
9820 HandleExpr(intrinsiccallNode, *intrinsiccallNode.Opnd(0)),
9821 HandleExpr(intrinsiccallNode, *intrinsiccallNode.Opnd(1)));
9822 return;
9823 case INTRN_C_stack_save:
9824 return;
9825 case INTRN_C_stack_restore:
9826 return;
9827 case INTRN_C___builtin_division_exception:
9828 SelectCDIVException();
9829 return;
9830 default:
9831 break;
9832 }
9833 std::vector<Operand *> operands; /* Temporary. Deallocated on return. */
9834 ListOperand *srcOpnds = CreateListOpnd(*GetFuncScopeAllocator());
9835 for (size_t i = 0; i < intrinsiccallNode.NumOpnds(); i++) {
9836 BaseNode *argExpr = intrinsiccallNode.Opnd(i);
9837 Operand *opnd = HandleExpr(intrinsiccallNode, *argExpr);
9838 operands.emplace_back(opnd);
9839 if (!opnd->IsRegister()) {
9840 opnd = &LoadIntoRegister(*opnd, argExpr->GetPrimType());
9841 }
9842 RegOperand *expRegOpnd = static_cast<RegOperand *>(opnd);
9843 srcOpnds->PushOpnd(*expRegOpnd);
9844 }
9845 CallReturnVector *retVals = &intrinsiccallNode.GetReturnVec();
9846
9847 switch (intrinsic) {
9848 case INTRN_MPL_ATOMIC_EXCHANGE_PTR: {
9849 BB *origFtBB = GetCurBB()->GetNext();
9850 Operand *loc = operands[kInsnFirstOpnd];
9851 Operand *newVal = operands[kInsnSecondOpnd];
9852 Operand *memOrd = operands[kInsnThirdOpnd];
9853
9854 MemOrd ord = OperandToMemOrd(*memOrd);
9855 bool isAcquire = MemOrdIsAcquire(ord);
9856 bool isRelease = MemOrdIsRelease(ord);
9857
9858 const PrimType kValPrimType = PTY_a64;
9859
9860 RegOperand &locReg = LoadIntoRegister(*loc, PTY_a64);
9861 /* Because there is no live analysis when -O1 */
9862 if (Globals::GetInstance()->GetOptimLevel() == CGOptions::kLevel0) {
9863 locReg.SetRegNotBBLocal();
9864 }
9865 MemOperand &locMem = GetOrCreateMemOpnd(MemOperand::kAddrModeBOi, k64BitSize, &locReg, nullptr,
9866 &GetOrCreateOfstOpnd(0, k32BitSize), nullptr);
9867 RegOperand &newValReg = LoadIntoRegister(*newVal, PTY_a64);
9868 if (Globals::GetInstance()->GetOptimLevel() == CGOptions::kLevel0) {
9869 newValReg.SetRegNotBBLocal();
9870 }
9871 GetCurBB()->SetKind(BB::kBBFallthru);
9872
9873 LabelIdx retryLabIdx = CreateLabeledBB(intrinsiccallNode);
9874
9875 RegOperand *oldVal = SelectLoadExcl(kValPrimType, locMem, isAcquire);
9876 if (Globals::GetInstance()->GetOptimLevel() == CGOptions::kLevel0) {
9877 oldVal->SetRegNotBBLocal();
9878 }
9879 RegOperand *succ = SelectStoreExcl(kValPrimType, locMem, newValReg, isRelease);
9880 if (Globals::GetInstance()->GetOptimLevel() == CGOptions::kLevel0) {
9881 succ->SetRegNotBBLocal();
9882 }
9883
9884 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wcbnz, *succ, GetOrCreateLabelOperand(retryLabIdx)));
9885 GetCurBB()->SetKind(BB::kBBIntrinsic);
9886 GetCurBB()->SetNext(origFtBB);
9887
9888 SaveReturnValueInLocal(*retVals, 0, kValPrimType, *oldVal, intrinsiccallNode);
9889 break;
9890 }
9891 case INTRN_C___atomic_exchange_n: {
9892 Operand *oldVal = SelectCAtomicExchangeN(intrinsiccallNode);
9893 auto primType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(intrinsiccallNode.GetTyIdx())->GetPrimType();
9894 uint32 regSize = GetPrimTypeBitSize(primType);
9895 SelectCopy(GetOrCreatePhysicalRegisterOperand(R0, regSize, kRegTyInt), primType, *oldVal, primType);
9896 break;
9897 }
9898 default: {
9899 CHECK_FATAL(false, "Intrinsic %d: %s not implemented by the AArch64 CG.", intrinsic,
9900 GetIntrinsicName(intrinsic));
9901 break;
9902 }
9903 }
9904 }
9905
SelectCclz(IntrinsicopNode & intrnNode)9906 Operand *AArch64CGFunc::SelectCclz(IntrinsicopNode &intrnNode)
9907 {
9908 BaseNode *argexpr = intrnNode.Opnd(0);
9909 PrimType ptype = argexpr->GetPrimType();
9910 Operand *opnd = HandleExpr(intrnNode, *argexpr);
9911 MOperator mop;
9912
9913 RegOperand &ldDest = CreateRegisterOperandOfType(ptype);
9914 if (opnd->IsMemoryAccessOperand()) {
9915 Insn &insn = GetInsnBuilder()->BuildInsn(PickLdInsn(GetPrimTypeBitSize(ptype), ptype), ldDest, *opnd);
9916 GetCurBB()->AppendInsn(insn);
9917 opnd = &ldDest;
9918 } else if (opnd->IsImmediate()) {
9919 SelectCopyImm(ldDest, *static_cast<ImmOperand *>(opnd), ptype);
9920 opnd = &ldDest;
9921 }
9922
9923 if (GetPrimTypeSize(ptype) == k4ByteSize) {
9924 mop = MOP_wclz;
9925 } else {
9926 mop = MOP_xclz;
9927 }
9928 RegOperand &dst = CreateRegisterOperandOfType(ptype);
9929 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mop, dst, *opnd));
9930 return &dst;
9931 }
9932
SelectCctz(IntrinsicopNode & intrnNode)9933 Operand *AArch64CGFunc::SelectCctz(IntrinsicopNode &intrnNode)
9934 {
9935 BaseNode *argexpr = intrnNode.Opnd(0);
9936 PrimType ptype = argexpr->GetPrimType();
9937 Operand *opnd = HandleExpr(intrnNode, *argexpr);
9938
9939 RegOperand &ldDest = CreateRegisterOperandOfType(ptype);
9940 if (opnd->IsMemoryAccessOperand()) {
9941 Insn &insn = GetInsnBuilder()->BuildInsn(PickLdInsn(GetPrimTypeBitSize(ptype), ptype), ldDest, *opnd);
9942 GetCurBB()->AppendInsn(insn);
9943 opnd = &ldDest;
9944 } else if (opnd->IsImmediate()) {
9945 SelectCopyImm(ldDest, *static_cast<ImmOperand *>(opnd), ptype);
9946 opnd = &ldDest;
9947 }
9948
9949 MOperator clzmop;
9950 MOperator rbitmop;
9951 if (GetPrimTypeSize(ptype) == k4ByteSize) {
9952 clzmop = MOP_wclz;
9953 rbitmop = MOP_wrbit;
9954 } else {
9955 clzmop = MOP_xclz;
9956 rbitmop = MOP_xrbit;
9957 }
9958 RegOperand &dst1 = CreateRegisterOperandOfType(ptype);
9959 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(rbitmop, dst1, *opnd));
9960 RegOperand &dst2 = CreateRegisterOperandOfType(ptype);
9961 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(clzmop, dst2, dst1));
9962 return &dst2;
9963 }
9964
SelectCpopcount(IntrinsicopNode & intrnNode)9965 Operand *AArch64CGFunc::SelectCpopcount(IntrinsicopNode &intrnNode)
9966 {
9967 CHECK_FATAL(false, "%s NIY", intrnNode.GetIntrinDesc().name);
9968 return nullptr;
9969 }
9970
SelectCparity(IntrinsicopNode & intrnNode)9971 Operand *AArch64CGFunc::SelectCparity(IntrinsicopNode &intrnNode)
9972 {
9973 CHECK_FATAL(false, "%s NIY", intrnNode.GetIntrinDesc().name);
9974 return nullptr;
9975 }
9976
SelectCclrsb(IntrinsicopNode & intrnNode)9977 Operand *AArch64CGFunc::SelectCclrsb(IntrinsicopNode &intrnNode)
9978 {
9979 BaseNode *argexpr = intrnNode.Opnd(0);
9980 PrimType ptype = argexpr->GetPrimType();
9981 Operand *opnd = HandleExpr(intrnNode, *argexpr);
9982
9983 RegOperand &ldDest = CreateRegisterOperandOfType(ptype);
9984 if (opnd->IsMemoryAccessOperand()) {
9985 Insn &insn = GetInsnBuilder()->BuildInsn(PickLdInsn(GetPrimTypeBitSize(ptype), ptype), ldDest, *opnd);
9986 GetCurBB()->AppendInsn(insn);
9987 opnd = &ldDest;
9988 } else if (opnd->IsImmediate()) {
9989 SelectCopyImm(ldDest, *static_cast<ImmOperand *>(opnd), ptype);
9990 opnd = &ldDest;
9991 }
9992
9993 bool is32Bit = (GetPrimTypeSize(ptype) == k4ByteSize);
9994 RegOperand &res = CreateRegisterOperandOfType(ptype);
9995 SelectMvn(res, *opnd, ptype);
9996 SelectAArch64Cmp(*opnd, GetZeroOpnd(is32Bit ? k32BitSize : k64BitSize), true, is32Bit ? k32BitSize : k64BitSize);
9997 SelectAArch64Select(*opnd, res, *opnd, GetCondOperand(CC_LT), true, is32Bit ? k32BitSize : k64BitSize);
9998 MOperator clzmop = (is32Bit ? MOP_wclz : MOP_xclz);
9999 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(clzmop, *opnd, *opnd));
10000 SelectSub(*opnd, *opnd, CreateImmOperand(1, is32Bit ? k32BitSize : k64BitSize, true), ptype);
10001 return opnd;
10002 }
10003
SelectCisaligned(IntrinsicopNode & intrnNode)10004 Operand *AArch64CGFunc::SelectCisaligned(IntrinsicopNode &intrnNode)
10005 {
10006 BaseNode *argexpr0 = intrnNode.Opnd(0);
10007 PrimType ptype0 = argexpr0->GetPrimType();
10008 Operand *opnd0 = HandleExpr(intrnNode, *argexpr0);
10009
10010 RegOperand &ldDest0 = CreateRegisterOperandOfType(ptype0);
10011 if (opnd0->IsMemoryAccessOperand()) {
10012 GetCurBB()->AppendInsn(
10013 GetInsnBuilder()->BuildInsn(PickLdInsn(GetPrimTypeBitSize(ptype0), ptype0), ldDest0, *opnd0));
10014 opnd0 = &ldDest0;
10015 } else if (opnd0->IsImmediate()) {
10016 SelectCopyImm(ldDest0, *static_cast<ImmOperand *>(opnd0), ptype0);
10017 opnd0 = &ldDest0;
10018 }
10019
10020 BaseNode *argexpr1 = intrnNode.Opnd(1);
10021 PrimType ptype1 = argexpr1->GetPrimType();
10022 Operand *opnd1 = HandleExpr(intrnNode, *argexpr1);
10023
10024 RegOperand &ldDest1 = CreateRegisterOperandOfType(ptype1);
10025 if (opnd1->IsMemoryAccessOperand()) {
10026 GetCurBB()->AppendInsn(
10027 GetInsnBuilder()->BuildInsn(PickLdInsn(GetPrimTypeBitSize(ptype1), ptype1), ldDest1, *opnd1));
10028 opnd1 = &ldDest1;
10029 } else if (opnd1->IsImmediate()) {
10030 SelectCopyImm(ldDest1, *static_cast<ImmOperand *>(opnd1), ptype1);
10031 opnd1 = &ldDest1;
10032 }
10033 // mov w4, #1
10034 RegOperand ®0 = CreateRegisterOperandOfType(PTY_i32);
10035 SelectCopyImm(reg0, CreateImmOperand(1, k32BitSize, true), PTY_i32);
10036 // sxtw x4, w4
10037 MOperator mOp = MOP_xsxtw64;
10038 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, reg0, reg0));
10039 // sub x3, x3, x4
10040 SelectSub(*opnd1, *opnd1, reg0, ptype1);
10041 // and x2, x2, x3
10042 SelectBand(*opnd0, *opnd0, *opnd1, ptype1);
10043 // mov w3, #0
10044 // sxtw x3, w3
10045 // cmp x2, x3
10046 SelectAArch64Cmp(*opnd0, GetZeroOpnd(k64BitSize), true, k64BitSize);
10047 // cset w2, EQ
10048 SelectAArch64CSet(*opnd0, GetCondOperand(CC_EQ), false);
10049 return opnd0;
10050 }
10051
SelectArithmeticAndLogical(Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType,Opcode op)10052 void AArch64CGFunc::SelectArithmeticAndLogical(Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType,
10053 Opcode op)
10054 {
10055 switch (op) {
10056 case OP_add:
10057 SelectAdd(resOpnd, opnd0, opnd1, primType);
10058 break;
10059 case OP_sub:
10060 SelectSub(resOpnd, opnd0, opnd1, primType);
10061 break;
10062 case OP_band:
10063 SelectBand(resOpnd, opnd0, opnd1, primType);
10064 break;
10065 case OP_bior:
10066 SelectBior(resOpnd, opnd0, opnd1, primType);
10067 break;
10068 case OP_bxor:
10069 SelectBxor(resOpnd, opnd0, opnd1, primType);
10070 break;
10071 default:
10072 CHECK_FATAL(false, "unconcerned opcode for arithmetical and logical insns");
10073 break;
10074 }
10075 }
10076
SelectAArch64CSyncFetch(const IntrinsicopNode & intrinopNode,Opcode op,bool fetchBefore)10077 Operand *AArch64CGFunc::SelectAArch64CSyncFetch(const IntrinsicopNode &intrinopNode, Opcode op, bool fetchBefore)
10078 {
10079 auto primType = intrinopNode.GetPrimType();
10080 /* Create BB which includes atomic built_in function */
10081 LabelIdx atomicBBLabIdx = CreateLabel();
10082 BB *atomicBB = CreateNewBB();
10083 atomicBB->SetKind(BB::kBBIf);
10084 atomicBB->SetAtomicBuiltIn();
10085 atomicBB->AddLabel(atomicBBLabIdx);
10086 SetLab2BBMap(static_cast<int32>(atomicBBLabIdx), *atomicBB);
10087 GetCurBB()->AppendBB(*atomicBB);
10088 /* keep variables inside same BB */
10089 if (GetCG()->GetOptimizeLevel() == CGOptions::kLevel0) {
10090 SetCurBB(*atomicBB);
10091 }
10092 /* handle built_in args */
10093 Operand *addrOpnd = HandleExpr(intrinopNode, *intrinopNode.GetNopndAt(kInsnFirstOpnd));
10094 Operand *valueOpnd = HandleExpr(intrinopNode, *intrinopNode.GetNopndAt(kInsnSecondOpnd));
10095 addrOpnd = &LoadIntoRegister(*addrOpnd, intrinopNode.GetNopndAt(kInsnFirstOpnd)->GetPrimType());
10096 valueOpnd = &LoadIntoRegister(*valueOpnd, intrinopNode.GetNopndAt(kInsnSecondOpnd)->GetPrimType());
10097 if (GetCG()->GetOptimizeLevel() != CGOptions::kLevel0) {
10098 SetCurBB(*atomicBB);
10099 }
10100 /* load from pointed address */
10101 auto primTypeP2Size = GetPrimTypeP2Size(primType);
10102 auto *regLoaded = &CreateRegisterOperandOfType(primType);
10103 auto &memOpnd = CreateMemOpnd(*static_cast<RegOperand *>(addrOpnd), 0, GetPrimTypeBitSize(primType));
10104 auto mOpLoad = PickLoadStoreExclInsn(primTypeP2Size, false, false);
10105 atomicBB->AppendInsn(GetInsnBuilder()->BuildInsn(mOpLoad, *regLoaded, memOpnd));
10106 /* update loaded value */
10107 auto *regOperated = &CreateRegisterOperandOfType(primType);
10108 SelectArithmeticAndLogical(*regOperated, *regLoaded, *valueOpnd, primType, op);
10109 /* store to pointed address */
10110 auto *accessStatus = &CreateRegisterOperandOfType(PTY_u32);
10111 auto mOpStore = PickLoadStoreExclInsn(primTypeP2Size, true, true);
10112 atomicBB->AppendInsn(GetInsnBuilder()->BuildInsn(mOpStore, *accessStatus, *regOperated, memOpnd));
10113 /* check the exclusive accsess status */
10114 auto &atomicBBOpnd = GetOrCreateLabelOperand(*atomicBB);
10115 atomicBB->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wcbnz, *accessStatus, atomicBBOpnd));
10116
10117 /* Data Memory Barrier */
10118 BB *nextBB = CreateNewBB();
10119 atomicBB->AppendBB(*nextBB);
10120 SetCurBB(*nextBB);
10121 nextBB->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_dmb_ish, AArch64CG::kMd[MOP_dmb_ish]));
10122 return fetchBefore ? regLoaded : regOperated;
10123 }
10124
SelectCSyncCmpSwap(const IntrinsicopNode & intrinopNode,bool retBool)10125 Operand *AArch64CGFunc::SelectCSyncCmpSwap(const IntrinsicopNode &intrinopNode, bool retBool)
10126 {
10127 PrimType primType = intrinopNode.GetNopndAt(kInsnSecondOpnd)->GetPrimType();
10128 LabelIdx atomicBBLabIdx = CreateLabel();
10129 /* Create BB which includes atomic built_in function */
10130 BB *atomicBB = CreateNewBB();
10131 atomicBB->SetKind(BB::kBBIf);
10132 atomicBB->SetAtomicBuiltIn();
10133 atomicBB->AddLabel(atomicBBLabIdx);
10134 SetLab2BBMap(static_cast<int32>(atomicBBLabIdx), *atomicBB);
10135 GetCurBB()->AppendBB(*atomicBB);
10136 /* keep variables inside same BB in O0 to avoid obtaining parameter value across BB blocks */
10137 if (GetCG()->GetOptimizeLevel() == CGOptions::kLevel0) {
10138 SetCurBB(*atomicBB);
10139 }
10140 /* handle built_in args */
10141 Operand *addrOpnd = HandleExpr(intrinopNode, *intrinopNode.GetNopndAt(kInsnFirstOpnd));
10142 Operand *oldVal = HandleExpr(intrinopNode, *intrinopNode.GetNopndAt(kInsnSecondOpnd));
10143
10144 PrimType nodePrimType = intrinopNode.GetPrimType();
10145 uint32 nodePrimTypeSize = GetPrimTypeSize(nodePrimType);
10146 // ensure the value is not changed after loading with extention
10147 if (IsSignedInteger(nodePrimType) && nodePrimTypeSize < k4ByteSize && GetCurBB() &&
10148 GetCurBB()->GetLastMachineInsn() && GetCurBB()->GetLastMachineInsn()->IsLoad()) {
10149 int64 maxIntVal = nodePrimTypeSize == k1ByteSize ? kMax8UnsignedImm : kMax16UnsignedImm;
10150 Operand &lastDestOpnd = GetCurBB()->GetLastMachineInsn()->GetOperand(kInsnFirstOpnd);
10151 DEBUG_ASSERT(lastDestOpnd.IsRegister(), "last insn's 1st operand is not register.");
10152 ImmOperand &immOpnd = CreateImmOperand(primType, maxIntVal);
10153 Insn &andInsn = GetInsnBuilder()->BuildInsn(MOP_wandrri12, lastDestOpnd, lastDestOpnd, immOpnd);
10154 GetCurBB()->AppendInsn(andInsn);
10155 }
10156
10157 Operand *newVal = HandleExpr(intrinopNode, *intrinopNode.GetNopndAt(kInsnThirdOpnd));
10158 if (GetCG()->GetOptimizeLevel() != CGOptions::kLevel0) {
10159 SetCurBB(*atomicBB);
10160 }
10161
10162 uint32 nodePrimTypeP2Size = GetPrimTypeP2Size(nodePrimType);
10163 /* ldxr */
10164 auto *regLoaded = &CreateRegisterOperandOfType(primType);
10165 auto &memOpnd = CreateMemOpnd(LoadIntoRegister(*addrOpnd, primType), 0, GetPrimTypeBitSize(primType));
10166 auto mOpLoad = PickLoadStoreExclInsn(nodePrimTypeP2Size, false, false);
10167 atomicBB->AppendInsn(GetInsnBuilder()->BuildInsn(mOpLoad, *regLoaded, memOpnd));
10168 Operand *regExtend = &CreateRegisterOperandOfType(primType);
10169 PrimType targetType = (oldVal->GetSize() <= k32BitSize) ? (IsSignedInteger(primType) ? PTY_i32 : PTY_u32)
10170 : (IsSignedInteger(primType) ? PTY_i64 : PTY_u64);
10171 SelectCvtInt2Int(nullptr, regExtend, regLoaded, primType, targetType);
10172 /* cmp */
10173 SelectAArch64Cmp(*regExtend, *oldVal, true, oldVal->GetSize());
10174 /* bne */
10175 Operand &rflag = GetOrCreateRflag();
10176 LabelIdx nextBBLableIdx = CreateLabel();
10177 LabelOperand &targetOpnd = GetOrCreateLabelOperand(nextBBLableIdx);
10178 atomicBB->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_bne, rflag, targetOpnd));
10179 /* stlxr */
10180 BB *stlxrBB = CreateNewBB();
10181 stlxrBB->SetKind(BB::kBBIf);
10182 atomicBB->AppendBB(*stlxrBB);
10183 SetCurBB(*stlxrBB);
10184 auto *accessStatus = &CreateRegisterOperandOfType(PTY_u32);
10185 auto &newRegVal = LoadIntoRegister(*newVal, primType);
10186 auto mOpStore = PickLoadStoreExclInsn(nodePrimTypeP2Size, true, true);
10187 stlxrBB->AppendInsn(GetInsnBuilder()->BuildInsn(mOpStore, *accessStatus, newRegVal, memOpnd));
10188 /* cbnz ==> check the exclusive accsess status */
10189 auto &atomicBBOpnd = GetOrCreateLabelOperand(*atomicBB);
10190 stlxrBB->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wcbnz, *accessStatus, atomicBBOpnd));
10191 /* Data Memory Barrier */
10192 BB *nextBB = CreateNewBB();
10193 nextBB->AddLabel(nextBBLableIdx);
10194 nextBB->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_dmb_ish, AArch64CG::kMd[MOP_dmb_ish]));
10195 /* special handle for boolean return type */
10196 if (intrinopNode.GetPrimType() == PTY_u1) {
10197 nextBB->AppendInsn(
10198 GetInsnBuilder()->BuildInsn(MOP_wandrri12, *regLoaded, *regLoaded, CreateImmOperand(PTY_u32, 1)));
10199 }
10200 SetLab2BBMap(static_cast<int32>(nextBBLableIdx), *nextBB);
10201 stlxrBB->AppendBB(*nextBB);
10202 SetCurBB(*nextBB);
10203 /* bool version return true if the comparison is successful and newval is written */
10204 if (retBool) {
10205 auto *retOpnd = &CreateRegisterOperandOfType(PTY_u32);
10206 SelectAArch64CSet(*retOpnd, GetCondOperand(CC_EQ), false);
10207 return retOpnd;
10208 }
10209 /* type version return the contents of *addrOpnd before the operation */
10210 return regLoaded;
10211 }
10212
SelectCSyncFetch(IntrinsicopNode & intrinopNode,Opcode op,bool fetchBefore)10213 Operand *AArch64CGFunc::SelectCSyncFetch(IntrinsicopNode &intrinopNode, Opcode op, bool fetchBefore)
10214 {
10215 return SelectAArch64CSyncFetch(intrinopNode, op, fetchBefore);
10216 }
10217
SelectCSyncBoolCmpSwap(IntrinsicopNode & intrinopNode)10218 Operand *AArch64CGFunc::SelectCSyncBoolCmpSwap(IntrinsicopNode &intrinopNode)
10219 {
10220 return SelectCSyncCmpSwap(intrinopNode, true);
10221 }
10222
SelectCSyncValCmpSwap(IntrinsicopNode & intrinopNode)10223 Operand *AArch64CGFunc::SelectCSyncValCmpSwap(IntrinsicopNode &intrinopNode)
10224 {
10225 return SelectCSyncCmpSwap(intrinopNode);
10226 }
10227
SelectCSyncLockTestSet(IntrinsicopNode & intrinopNode,PrimType pty)10228 Operand *AArch64CGFunc::SelectCSyncLockTestSet(IntrinsicopNode &intrinopNode, PrimType pty)
10229 {
10230 auto primType = intrinopNode.GetPrimType();
10231 /*handle builtin args */
10232 Operand *addrOpnd = HandleExpr(intrinopNode, *intrinopNode.GetNopndAt(kInsnFirstOpnd));
10233 Operand *valueOpnd = HandleExpr(intrinopNode, *intrinopNode.GetNopndAt(kInsnSecondOpnd));
10234 addrOpnd = &LoadIntoRegister(*addrOpnd, intrinopNode.GetNopndAt(kInsnFirstOpnd)->GetPrimType());
10235 valueOpnd = &LoadIntoRegister(*valueOpnd, intrinopNode.GetNopndAt(kInsnSecondOpnd)->GetPrimType());
10236
10237 /* Create BB which includes atomic built_in function */
10238 LabelIdx atomicBBLabIdx = CreateLabel();
10239 BB *atomicBB = CreateNewBB();
10240 atomicBB->SetKind(BB::kBBIf);
10241 atomicBB->SetAtomicBuiltIn();
10242 atomicBB->AddLabel(atomicBBLabIdx);
10243 SetLab2BBMap(static_cast<int32>(atomicBBLabIdx), *atomicBB);
10244 GetCurBB()->AppendBB(*atomicBB);
10245 SetCurBB(*atomicBB);
10246 /* load from pointed address */
10247 auto primTypeP2Size = GetPrimTypeP2Size(primType);
10248 auto *regLoaded = &CreateRegisterOperandOfType(primType);
10249 auto &memOpnd = CreateMemOpnd(*static_cast<RegOperand *>(addrOpnd), 0, GetPrimTypeBitSize(primType));
10250 auto mOpLoad = PickLoadStoreExclInsn(primTypeP2Size, false, false);
10251 atomicBB->AppendInsn(GetInsnBuilder()->BuildInsn(mOpLoad, *regLoaded, memOpnd));
10252 /* store to pointed address */
10253 auto *accessStatus = &CreateRegisterOperandOfType(PTY_u32);
10254 auto mOpStore = PickLoadStoreExclInsn(primTypeP2Size, true, false);
10255 atomicBB->AppendInsn(GetInsnBuilder()->BuildInsn(mOpStore, *accessStatus, *valueOpnd, memOpnd));
10256 /* check the exclusive accsess status */
10257 auto &atomicBBOpnd = GetOrCreateLabelOperand(*atomicBB);
10258 atomicBB->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_wcbnz, *accessStatus, atomicBBOpnd));
10259
10260 /* Data Memory Barrier */
10261 BB *nextBB = CreateNewBB();
10262 atomicBB->AppendBB(*nextBB);
10263 SetCurBB(*nextBB);
10264 nextBB->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_dmb_ish, AArch64CG::kMd[MOP_dmb_ish]));
10265 return regLoaded;
10266 }
10267
SelectCSyncLockRelease(const IntrinsiccallNode & intrinsiccall,PrimType primType)10268 void AArch64CGFunc::SelectCSyncLockRelease(const IntrinsiccallNode &intrinsiccall, PrimType primType)
10269 {
10270 auto *addrOpnd = HandleExpr(intrinsiccall, *intrinsiccall.GetNopndAt(kInsnFirstOpnd));
10271 auto primTypeBitSize = GetPrimTypeBitSize(primType);
10272 auto mOp = PickStInsn(primTypeBitSize, primType, AArch64isa::kMoRelease);
10273 auto &zero = GetZeroOpnd(primTypeBitSize);
10274 auto &memOpnd = CreateMemOpnd(LoadIntoRegister(*addrOpnd, primType), 0, primTypeBitSize);
10275 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, zero, memOpnd));
10276 }
10277
SelectCSyncSynchronize(IntrinsicopNode & intrinopNode)10278 Operand *AArch64CGFunc::SelectCSyncSynchronize(IntrinsicopNode &intrinopNode)
10279 {
10280 (void)intrinopNode;
10281 CHECK_FATAL(false, "have not implement SelectCSyncSynchronize yet");
10282 return nullptr;
10283 }
10284
PickMemOrder(std::memory_order memOrder,bool isLdr) const10285 AArch64isa::MemoryOrdering AArch64CGFunc::PickMemOrder(std::memory_order memOrder, bool isLdr) const
10286 {
10287 switch (memOrder) {
10288 case std::memory_order_relaxed:
10289 return AArch64isa::kMoNone;
10290 case std::memory_order_consume:
10291 case std::memory_order_acquire:
10292 return isLdr ? AArch64isa::kMoAcquire : AArch64isa::kMoNone;
10293 case std::memory_order_release:
10294 return isLdr ? AArch64isa::kMoNone : AArch64isa::kMoRelease;
10295 case std::memory_order_acq_rel:
10296 case std::memory_order_seq_cst:
10297 return isLdr ? AArch64isa::kMoAcquire : AArch64isa::kMoRelease;
10298 default:
10299 CHECK_FATAL(false, "unexpected memorder");
10300 return AArch64isa::kMoNone;
10301 }
10302 }
10303
10304 /*
10305 * regassign %1 (intrinsicop C___Atomic_Load_N(ptr, memorder))
10306 * ====> %1 = *ptr
10307 * let %1 -> x0
10308 * let ptr -> x1
10309 * implement to asm: ldr/ldar x0, [x1]
10310 * a load-acquire would replace ldr if memorder is not 0
10311 */
SelectCAtomicLoadN(IntrinsicopNode & intrinsicopNode)10312 Operand *AArch64CGFunc::SelectCAtomicLoadN(IntrinsicopNode &intrinsicopNode)
10313 {
10314 auto *addrOpnd = HandleExpr(intrinsicopNode, *intrinsicopNode.Opnd(0));
10315 auto *memOrderOpnd = intrinsicopNode.Opnd(1);
10316 auto primType = intrinsicopNode.GetPrimType();
10317 std::memory_order memOrder = std::memory_order_seq_cst;
10318 if (memOrderOpnd->IsConstval()) {
10319 auto *memOrderConst = static_cast<MIRIntConst *>(static_cast<ConstvalNode *>(memOrderOpnd)->GetConstVal());
10320 memOrder = static_cast<std::memory_order>(memOrderConst->GetExtValue());
10321 }
10322 return SelectAtomicLoad(*addrOpnd, primType, PickMemOrder(memOrder, true));
10323 }
10324
10325 /*
10326 * regassign %1 (intrinsicop C___Atomic_exchange_n(ptr, val, memorder))
10327 * ====> %1 = *ptr; *ptr = val;
10328 * let %1 -> x0
10329 * let ptr -> x1
10330 * let val -> x2
10331 * implement to asm:
10332 * ldr/ldar x0, [x1]
10333 * str/stlr x2, [x1]
10334 * a load-acquire would replace ldr if acquire needed
10335 * a store-relase would replace str if release needed
10336 */
SelectCAtomicExchangeN(const IntrinsiccallNode & intrinsiccallNode)10337 Operand *AArch64CGFunc::SelectCAtomicExchangeN(const IntrinsiccallNode &intrinsiccallNode)
10338 {
10339 auto primType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(intrinsiccallNode.GetTyIdx())->GetPrimType();
10340 auto *addrOpnd = HandleExpr(intrinsiccallNode, *intrinsiccallNode.Opnd(0));
10341 auto *valueOpnd = HandleExpr(intrinsiccallNode, *intrinsiccallNode.Opnd(1));
10342 auto *memOrderOpnd = intrinsiccallNode.Opnd(kInsnThirdOpnd);
10343
10344 /* slect memry order */
10345 std::memory_order memOrder = std::memory_order_seq_cst;
10346 if (memOrderOpnd->IsConstval()) {
10347 auto *memOrderConst = static_cast<MIRIntConst *>(static_cast<ConstvalNode *>(memOrderOpnd)->GetConstVal());
10348 memOrder = static_cast<std::memory_order>(memOrderConst->GetExtValue());
10349 }
10350 auto *result = SelectAtomicLoad(*addrOpnd, primType, PickMemOrder(memOrder, true));
10351 SelectAtomicStore(*valueOpnd, *addrOpnd, primType, PickMemOrder(memOrder, false));
10352 return result;
10353 }
10354
SelectAtomicLoad(Operand & addrOpnd,PrimType primType,AArch64isa::MemoryOrdering memOrder)10355 Operand *AArch64CGFunc::SelectAtomicLoad(Operand &addrOpnd, PrimType primType, AArch64isa::MemoryOrdering memOrder)
10356 {
10357 auto mOp = PickLdInsn(GetPrimTypeBitSize(primType), primType, memOrder);
10358 auto &memOpnd = CreateMemOpnd(LoadIntoRegister(addrOpnd, PTY_a64), 0, k64BitSize);
10359 auto *resultOpnd = &CreateRegisterOperandOfType(primType);
10360 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, *resultOpnd, memOpnd));
10361 return resultOpnd;
10362 }
10363
SelectCReturnAddress(IntrinsicopNode & intrinopNode)10364 Operand *AArch64CGFunc::SelectCReturnAddress(IntrinsicopNode &intrinopNode)
10365 {
10366 if (intrinopNode.GetIntrinsic() == INTRN_C__builtin_extract_return_addr) {
10367 DEBUG_ASSERT(intrinopNode.GetNumOpnds() == 1, "expect one parameter");
10368 Operand *addrOpnd = HandleExpr(intrinopNode, *intrinopNode.GetNopndAt(kInsnFirstOpnd));
10369 return &LoadIntoRegister(*addrOpnd, PTY_a64);
10370 } else if (intrinopNode.GetIntrinsic() == INTRN_C__builtin_return_address) {
10371 BaseNode *argexpr0 = intrinopNode.Opnd(0);
10372 while (!argexpr0->IsLeaf()) {
10373 argexpr0 = argexpr0->Opnd(0);
10374 }
10375 CHECK_FATAL(argexpr0->IsConstval(), "Invalid argument of __builtin_return_address");
10376 auto &constNode = static_cast<ConstvalNode &>(*argexpr0);
10377 DEBUG_ASSERT(constNode.GetConstVal()->GetKind() == kConstInt, "expect MIRIntConst does not support float yet");
10378 MIRIntConst *mirIntConst = safe_cast<MIRIntConst>(constNode.GetConstVal());
10379 DEBUG_ASSERT(mirIntConst != nullptr, "nullptr checking");
10380 int64 scale = mirIntConst->GetExtValue();
10381 /*
10382 * Do not support getting return address with a nonzero argument
10383 * inline / tail call opt will destory this behavior
10384 */
10385 CHECK_FATAL(scale == 0, "Do not support recursion");
10386 Operand *resReg = &static_cast<Operand &>(CreateRegisterOperandOfType(PTY_i64));
10387 SelectCopy(*resReg, PTY_i64, GetOrCreatePhysicalRegisterOperand(RLR, k64BitSize, kRegTyInt), PTY_i64);
10388 return resReg;
10389 }
10390 return nullptr;
10391 }
10392
SelectCalignup(IntrinsicopNode & intrnNode)10393 Operand *AArch64CGFunc::SelectCalignup(IntrinsicopNode &intrnNode)
10394 {
10395 return SelectAArch64align(intrnNode, true);
10396 }
10397
SelectCaligndown(IntrinsicopNode & intrnNode)10398 Operand *AArch64CGFunc::SelectCaligndown(IntrinsicopNode &intrnNode)
10399 {
10400 return SelectAArch64align(intrnNode, false);
10401 }
10402
SelectAArch64align(const IntrinsicopNode & intrnNode,bool isUp)10403 Operand *AArch64CGFunc::SelectAArch64align(const IntrinsicopNode &intrnNode, bool isUp)
10404 {
10405 /* Handle Two args */
10406 BaseNode *argexpr0 = intrnNode.Opnd(0);
10407 PrimType ptype0 = argexpr0->GetPrimType();
10408 Operand *opnd0 = HandleExpr(intrnNode, *argexpr0);
10409 PrimType resultPtype = intrnNode.GetPrimType();
10410 RegOperand &ldDest0 = LoadIntoRegister(*opnd0, ptype0);
10411
10412 BaseNode *argexpr1 = intrnNode.Opnd(1);
10413 PrimType ptype1 = argexpr1->GetPrimType();
10414 Operand *opnd1 = HandleExpr(intrnNode, *argexpr1);
10415 RegOperand &arg1 = LoadIntoRegister(*opnd1, ptype1);
10416 DEBUG_ASSERT(IsPrimitiveInteger(ptype0) && IsPrimitiveInteger(ptype1), "align integer type only");
10417 Operand *ldDest1 = &static_cast<Operand &>(CreateRegisterOperandOfType(ptype0));
10418 SelectCvtInt2Int(nullptr, ldDest1, &arg1, ptype1, ptype0);
10419
10420 Operand *resultReg = &static_cast<Operand &>(CreateRegisterOperandOfType(ptype0));
10421 Operand &immReg = CreateImmOperand(1, GetPrimTypeBitSize(ptype0), true);
10422 /* Do alignment x0 -- value to be aligned x1 -- alignment */
10423 if (isUp) {
10424 /* add res, x0, x1 */
10425 SelectAdd(*resultReg, ldDest0, *ldDest1, ptype0);
10426 /* sub res, res, 1 */
10427 SelectSub(*resultReg, *resultReg, immReg, ptype0);
10428 }
10429 Operand *tempReg = &static_cast<Operand &>(CreateRegisterOperandOfType(ptype0));
10430 /* sub temp, x1, 1 */
10431 SelectSub(*tempReg, *ldDest1, immReg, ptype0);
10432 /* mvn temp, temp */
10433 SelectMvn(*tempReg, *tempReg, ptype0);
10434 /* and res, res, temp */
10435 if (isUp) {
10436 SelectBand(*resultReg, *resultReg, *tempReg, ptype0);
10437 } else {
10438 SelectBand(*resultReg, ldDest0, *tempReg, ptype0);
10439 }
10440 if (resultPtype != ptype0) {
10441 SelectCvtInt2Int(&intrnNode, resultReg, resultReg, ptype0, resultPtype);
10442 }
10443 return resultReg;
10444 }
10445
SelectCDIVException()10446 void AArch64CGFunc::SelectCDIVException()
10447 {
10448 uint32 breakImm = 1000;
10449 ImmOperand &immOpnd = CreateImmOperand(breakImm, maplebe::k16BitSize, false);
10450 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_brk, immOpnd));
10451 }
10452
10453 /*
10454 * NOTE: consider moving the following things into aarch64_cg.cpp They may
10455 * serve not only inrinsics, but other MapleIR instructions as well.
10456 * Do it as if we are adding a label in straight-line assembly code.
10457 */
CreateLabeledBB(StmtNode & stmt)10458 LabelIdx AArch64CGFunc::CreateLabeledBB(StmtNode &stmt)
10459 {
10460 LabelIdx labIdx = CreateLabel();
10461 BB *newBB = StartNewBBImpl(false, stmt);
10462 newBB->AddLabel(labIdx);
10463 SetLab2BBMap(labIdx, *newBB);
10464 SetCurBB(*newBB);
10465 return labIdx;
10466 }
10467
10468 /* Save value into the local variable for the index-th return value; */
SaveReturnValueInLocal(CallReturnVector & retVals,size_t index,PrimType primType,Operand & value,StmtNode & parentStmt)10469 void AArch64CGFunc::SaveReturnValueInLocal(CallReturnVector &retVals, size_t index, PrimType primType, Operand &value,
10470 StmtNode &parentStmt)
10471 {
10472 CallReturnPair &pair = retVals.at(index);
10473 BB tempBB(static_cast<uint32>(-1), *GetFuncScopeAllocator());
10474 BB *realCurBB = GetCurBB();
10475 CHECK_FATAL(!pair.second.IsReg(), "NYI");
10476 Operand *destOpnd = &value;
10477 /* for O0 ,corss-BB var is not support, do extra store/load but why new BB */
10478 if (GetCG()->GetOptimizeLevel() == CGOptions::kLevel0) {
10479 MIRSymbol *symbol = GetFunction().GetLocalOrGlobalSymbol(pair.first);
10480 DEBUG_ASSERT(symbol != nullptr, "symbol should not be nullptr");
10481 MIRType *sPty = symbol->GetType();
10482 PrimType ty = symbol->GetType()->GetPrimType();
10483 if (sPty->GetKind() == kTypeStruct || sPty->GetKind() == kTypeUnion) {
10484 MIRStructType *structType = static_cast<MIRStructType *>(sPty);
10485 ty = structType->GetFieldType(pair.second.GetFieldID())->GetPrimType();
10486 } else if (sPty->GetKind() == kTypeClass) {
10487 CHECK_FATAL(false, "unsuppotr type for inlineasm / intrinsic");
10488 }
10489 RegOperand &tempReg = CreateVirtualRegisterOperand(NewVReg(GetRegTyFromPrimTy(ty), GetPrimTypeSize(ty)));
10490 SelectCopy(tempReg, ty, value, ty);
10491 destOpnd = &tempReg;
10492 }
10493 SetCurBB(tempBB);
10494 SelectDassign(pair.first, pair.second.GetFieldID(), primType, *destOpnd);
10495
10496 CHECK_FATAL(realCurBB->GetNext() == nullptr, "current BB must has not nextBB");
10497 realCurBB->SetLastStmt(parentStmt);
10498 realCurBB->SetNext(StartNewBBImpl(true, parentStmt));
10499 realCurBB->GetNext()->SetKind(BB::kBBFallthru);
10500 realCurBB->GetNext()->SetPrev(realCurBB);
10501
10502 realCurBB->GetNext()->InsertAtBeginning(*GetCurBB());
10503 /* restore it */
10504 SetCurBB(*realCurBB->GetNext());
10505 }
10506
10507 /* The following are translation of LL/SC and atomic RMW operations */
OperandToMemOrd(Operand & opnd) const10508 MemOrd AArch64CGFunc::OperandToMemOrd(Operand &opnd) const
10509 {
10510 CHECK_FATAL(opnd.IsImmediate(), "Memory order must be an int constant.");
10511 auto immOpnd = static_cast<ImmOperand *>(&opnd);
10512 int32 val = immOpnd->GetValue();
10513 CHECK_FATAL(val >= 0, "val must be non-negtive");
10514 return MemOrdFromU32(static_cast<uint32>(val));
10515 }
10516
10517 /*
10518 * Generate ldxr or ldaxr instruction.
10519 * byte_p2x: power-of-2 size of operand in bytes (0: 1B, 1: 2B, 2: 4B, 3: 8B).
10520 */
PickLoadStoreExclInsn(uint32 byteP2Size,bool store,bool acqRel) const10521 MOperator AArch64CGFunc::PickLoadStoreExclInsn(uint32 byteP2Size, bool store, bool acqRel) const
10522 {
10523 CHECK_FATAL(byteP2Size < kIntByteSizeDimension, "Illegal argument p2size: %d", byteP2Size);
10524
10525 static MOperator operators[4][2][2] = {{{MOP_wldxrb, MOP_wldaxrb}, {MOP_wstxrb, MOP_wstlxrb}},
10526 {{MOP_wldxrh, MOP_wldaxrh}, {MOP_wstxrh, MOP_wstlxrh}},
10527 {{MOP_wldxr, MOP_wldaxr}, {MOP_wstxr, MOP_wstlxr}},
10528 {{MOP_xldxr, MOP_xldaxr}, {MOP_xstxr, MOP_xstlxr}}};
10529
10530 MOperator optr = operators[byteP2Size][store][acqRel];
10531 CHECK_FATAL(optr != MOP_undef, "Unsupported type p2size: %d", byteP2Size);
10532
10533 return optr;
10534 }
10535
SelectLoadExcl(PrimType valPrimType,MemOperand & loc,bool acquire)10536 RegOperand *AArch64CGFunc::SelectLoadExcl(PrimType valPrimType, MemOperand &loc, bool acquire)
10537 {
10538 uint32 p2size = GetPrimTypeP2Size(valPrimType);
10539
10540 RegOperand &result = CreateRegisterOperandOfType(valPrimType);
10541 MOperator mOp = PickLoadStoreExclInsn(p2size, false, acquire);
10542 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, result, loc));
10543
10544 return &result;
10545 }
10546
SelectStoreExcl(PrimType valPty,MemOperand & loc,RegOperand & newVal,bool release)10547 RegOperand *AArch64CGFunc::SelectStoreExcl(PrimType valPty, MemOperand &loc, RegOperand &newVal, bool release)
10548 {
10549 uint32 p2size = GetPrimTypeP2Size(valPty);
10550
10551 /* the result (success/fail) is to be stored in a 32-bit register */
10552 RegOperand &result = CreateRegisterOperandOfType(PTY_u32);
10553
10554 MOperator mOp = PickLoadStoreExclInsn(p2size, true, release);
10555 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(mOp, result, newVal, loc));
10556
10557 return &result;
10558 }
10559
GetRegisterType(regno_t reg) const10560 RegType AArch64CGFunc::GetRegisterType(regno_t reg) const
10561 {
10562 if (AArch64isa::IsPhysicalRegister(reg)) {
10563 return AArch64isa::GetRegType(static_cast<AArch64reg>(reg));
10564 } else if (reg == kRFLAG) {
10565 return kRegTyCc;
10566 } else {
10567 return CGFunc::GetRegisterType(reg);
10568 }
10569 }
10570
LoadStructCopyBase(const MIRSymbol & symbol,int64 offset,int dataSize)10571 MemOperand &AArch64CGFunc::LoadStructCopyBase(const MIRSymbol &symbol, int64 offset, int dataSize)
10572 {
10573 /* For struct formals > 16 bytes, this is the pointer to the struct copy. */
10574 /* Load the base pointer first. */
10575 RegOperand *vreg = &CreateVirtualRegisterOperand(NewVReg(kRegTyInt, k8ByteSize));
10576 MemOperand *baseMemOpnd = &GetOrCreateMemOpnd(symbol, 0, k64BitSize);
10577 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(PickLdInsn(k64BitSize, PTY_i64), *vreg, *baseMemOpnd));
10578 /* Create the indirect load mem opnd from the base pointer. */
10579 return CreateMemOpnd(*vreg, offset, static_cast<uint32>(dataSize));
10580 }
10581
10582 /* For long branch, insert an unconditional branch.
10583 * From To
10584 * cond_br targe_label reverse_cond_br fallthru_label
10585 * fallthruBB unconditional br target_label
10586 * fallthru_label:
10587 * fallthruBB
10588 */
InsertJumpPad(Insn * insn)10589 void AArch64CGFunc::InsertJumpPad(Insn *insn)
10590 {
10591 BB *bb = insn->GetBB();
10592 DEBUG_ASSERT(bb, "instruction has no bb");
10593 DEBUG_ASSERT(bb->GetKind() == BB::kBBIf || bb->GetKind() == BB::kBBGoto,
10594 "instruction is in neither if bb nor goto bb");
10595 if (bb->GetKind() == BB::kBBGoto) {
10596 return;
10597 }
10598 DEBUG_ASSERT(bb->NumSuccs() == k2ByteSize, "if bb should have 2 successors");
10599
10600 BB *longBrBB = CreateNewBB();
10601
10602 BB *fallthruBB = bb->GetNext();
10603 LabelIdx fallthruLBL = fallthruBB->GetLabIdx();
10604 if (fallthruLBL == 0) {
10605 fallthruLBL = CreateLabel();
10606 SetLab2BBMap(static_cast<int32>(fallthruLBL), *fallthruBB);
10607 fallthruBB->AddLabel(fallthruLBL);
10608 }
10609
10610 BB *targetBB;
10611 if (bb->GetSuccs().front() == fallthruBB) {
10612 targetBB = bb->GetSuccs().back();
10613 } else {
10614 targetBB = bb->GetSuccs().front();
10615 }
10616 LabelIdx targetLBL = targetBB->GetLabIdx();
10617 if (targetLBL == 0) {
10618 targetLBL = CreateLabel();
10619 SetLab2BBMap(static_cast<int32>(targetLBL), *targetBB);
10620 targetBB->AddLabel(targetLBL);
10621 }
10622
10623 // Adjustment on br and CFG
10624 bb->RemoveSuccs(*targetBB);
10625 bb->PushBackSuccs(*longBrBB);
10626 bb->SetNext(longBrBB);
10627 // reverse cond br targeting fallthruBB
10628 uint32 targetIdx = AArch64isa::GetJumpTargetIdx(*insn);
10629 MOperator mOp = AArch64isa::FlipConditionOp(insn->GetMachineOpcode());
10630 insn->SetMOP(AArch64CG::kMd[mOp]);
10631 LabelOperand &fallthruBBLBLOpnd = GetOrCreateLabelOperand(fallthruLBL);
10632 insn->SetOperand(targetIdx, fallthruBBLBLOpnd);
10633
10634 longBrBB->PushBackPreds(*bb);
10635 longBrBB->PushBackSuccs(*targetBB);
10636 LabelOperand &targetLBLOpnd = GetOrCreateLabelOperand(targetLBL);
10637 longBrBB->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xuncond, targetLBLOpnd));
10638 longBrBB->SetPrev(bb);
10639 longBrBB->SetNext(fallthruBB);
10640 longBrBB->SetKind(BB::kBBGoto);
10641
10642 fallthruBB->SetPrev(longBrBB);
10643
10644 targetBB->RemovePreds(*bb);
10645 targetBB->PushBackPreds(*longBrBB);
10646 }
10647
AdjustOneElementVectorOperand(PrimType oType,RegOperand * opnd)10648 RegOperand *AArch64CGFunc::AdjustOneElementVectorOperand(PrimType oType, RegOperand *opnd)
10649 {
10650 RegOperand *resCvt = &CreateRegisterOperandOfType(oType);
10651 Insn *insnCvt = &GetInsnBuilder()->BuildInsn(MOP_xvmovrd, *resCvt, *opnd);
10652 GetCurBB()->AppendInsn(*insnCvt);
10653 return resCvt;
10654 }
10655
SelectOneElementVectorCopy(Operand * src,PrimType sType)10656 RegOperand *AArch64CGFunc::SelectOneElementVectorCopy(Operand *src, PrimType sType)
10657 {
10658 RegOperand *res = &CreateRegisterOperandOfType(PTY_f64);
10659 SelectCopy(*res, PTY_f64, *src, sType);
10660 static_cast<RegOperand *>(res)->SetIF64Vec();
10661 return res;
10662 }
10663
SelectVectorAbs(PrimType rType,Operand * o1)10664 RegOperand *AArch64CGFunc::SelectVectorAbs(PrimType rType, Operand *o1)
10665 {
10666 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
10667 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
10668 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(rType); /* vector operand 1 */
10669
10670 MOperator mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vabsvv : MOP_vabsuu;
10671 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
10672 vInsn.AddOpndChain(*res).AddOpndChain(*o1);
10673 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1);
10674 GetCurBB()->AppendInsn(vInsn);
10675 return res;
10676 }
10677
SelectVectorAddLong(PrimType rType,Operand * o1,Operand * o2,PrimType otyp,bool isLow)10678 RegOperand *AArch64CGFunc::SelectVectorAddLong(PrimType rType, Operand *o1, Operand *o2, PrimType otyp, bool isLow)
10679 {
10680 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result type */
10681 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
10682 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(otyp); /* vector operand 1 */
10683 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(otyp); /* vector operand 2 */
10684 MOperator mOp;
10685 if (isLow) {
10686 mOp = IsUnsignedInteger(rType) ? MOP_vuaddlvuu : MOP_vsaddlvuu;
10687 } else {
10688 mOp = IsUnsignedInteger(rType) ? MOP_vuaddl2vvv : MOP_vsaddl2vvv;
10689 }
10690 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
10691 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*o2);
10692 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1).PushRegSpecEntry(vecSpec2);
10693 GetCurBB()->AppendInsn(vInsn);
10694 return res;
10695 }
10696
SelectVectorAddWiden(Operand * o1,PrimType otyp1,Operand * o2,PrimType otyp2,bool isLow)10697 RegOperand *AArch64CGFunc::SelectVectorAddWiden(Operand *o1, PrimType otyp1, Operand *o2, PrimType otyp2, bool isLow)
10698 {
10699 RegOperand *res = &CreateRegisterOperandOfType(otyp1); /* restype is same as o1 */
10700 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(otyp1);
10701 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(otyp1); /* vector operand 1 */
10702 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(otyp2); /* vector operand 2 */
10703
10704 MOperator mOp;
10705 if (isLow) {
10706 mOp = IsUnsignedInteger(otyp1) ? MOP_vuaddwvvu : MOP_vsaddwvvu;
10707 } else {
10708 mOp = IsUnsignedInteger(otyp1) ? MOP_vuaddw2vvv : MOP_vsaddw2vvv;
10709 }
10710 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
10711 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*o2);
10712 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1).PushRegSpecEntry(vecSpec2);
10713 GetCurBB()->AppendInsn(vInsn);
10714 return res;
10715 }
10716
SelectVectorImmMov(PrimType rType,Operand * src,PrimType sType)10717 RegOperand *AArch64CGFunc::SelectVectorImmMov(PrimType rType, Operand *src, PrimType sType)
10718 {
10719 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
10720 VectorRegSpec *vecSpec = GetMemoryPool()->New<VectorRegSpec>(rType);
10721 int64 val = static_cast<ImmOperand *>(src)->GetValue();
10722 /* copy the src imm operand to a reg if out of range */
10723 if ((GetVecEleSize(rType) >= k64BitSize) || (GetPrimTypeSize(sType) > k4ByteSize && val != 0) ||
10724 (val < kMinImmVal || val > kMaxImmVal)) {
10725 Operand *reg = &CreateRegisterOperandOfType(sType);
10726 SelectCopy(*reg, sType, *src, sType);
10727 return SelectVectorRegMov(rType, reg, sType);
10728 }
10729
10730 MOperator mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vmovvi : MOP_vmovui;
10731 if (GetVecEleSize(rType) == k8BitSize && val < 0) {
10732 src = &CreateImmOperand(static_cast<uint8>(val), k8BitSize, true);
10733 } else if (val < 0) {
10734 src = &CreateImmOperand(-(val + 1), k8BitSize, true);
10735 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vnotvi : MOP_vnotui;
10736 }
10737
10738 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
10739 vInsn.AddOpndChain(*res).AddOpndChain(*src);
10740 vInsn.PushRegSpecEntry(vecSpec);
10741 GetCurBB()->AppendInsn(vInsn);
10742 return res;
10743 }
10744
SelectVectorRegMov(PrimType rType,Operand * src,PrimType sType)10745 RegOperand *AArch64CGFunc::SelectVectorRegMov(PrimType rType, Operand *src, PrimType sType)
10746 {
10747 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
10748 VectorRegSpec *vecSpec = GetMemoryPool()->New<VectorRegSpec>(rType);
10749
10750 MOperator mOp;
10751 if (GetPrimTypeSize(sType) > k4ByteSize) {
10752 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vxdupvr : MOP_vxdupur;
10753 } else {
10754 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vwdupvr : MOP_vwdupur;
10755 }
10756
10757 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
10758 vInsn.AddOpndChain(*res).AddOpndChain(*src);
10759 vInsn.PushRegSpecEntry(vecSpec);
10760 GetCurBB()->AppendInsn(vInsn);
10761 return res;
10762 }
10763
SelectVectorFromScalar(PrimType rType,Operand * src,PrimType sType)10764 RegOperand *AArch64CGFunc::SelectVectorFromScalar(PrimType rType, Operand *src, PrimType sType)
10765 {
10766 if (!IsPrimitiveVector(rType)) {
10767 return SelectOneElementVectorCopy(src, sType);
10768 } else if (src->IsConstImmediate()) {
10769 return SelectVectorImmMov(rType, src, sType);
10770 } else {
10771 return SelectVectorRegMov(rType, src, sType);
10772 }
10773 }
10774
SelectVectorDup(PrimType rType,Operand * src,bool getLow)10775 RegOperand *AArch64CGFunc::SelectVectorDup(PrimType rType, Operand *src, bool getLow)
10776 {
10777 PrimType oType = rType;
10778 rType = FilterOneElementVectorType(oType);
10779 RegOperand *res = &CreateRegisterOperandOfType(rType);
10780 VectorRegSpec *vecSpecSrc = GetMemoryPool()->New<VectorRegSpec>(k2ByteSize, k64BitSize, getLow ? 0 : 1);
10781
10782 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(MOP_vduprv, AArch64CG::kMd[MOP_vduprv]);
10783 vInsn.AddOpndChain(*res).AddOpndChain(*src);
10784 vInsn.PushRegSpecEntry(vecSpecSrc);
10785 GetCurBB()->AppendInsn(vInsn);
10786 if (oType != rType) {
10787 res = AdjustOneElementVectorOperand(oType, res);
10788 static_cast<RegOperand *>(res)->SetIF64Vec();
10789 }
10790 return res;
10791 }
10792
SelectVectorGetElement(PrimType rType,Operand * src,PrimType sType,int32 lane)10793 RegOperand *AArch64CGFunc::SelectVectorGetElement(PrimType rType, Operand *src, PrimType sType, int32 lane)
10794 {
10795 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
10796 VectorRegSpec *vecSpecSrc = GetMemoryPool()->New<VectorRegSpec>(sType, lane); /* vector operand */
10797
10798 MOperator mop;
10799 if (!IsPrimitiveVector(sType)) {
10800 mop = MOP_xmovrr;
10801 } else if (GetPrimTypeBitSize(rType) >= k64BitSize) {
10802 mop = MOP_vxmovrv;
10803 } else {
10804 mop = (GetPrimTypeBitSize(sType) > k64BitSize) ? MOP_vwmovrv : MOP_vwmovru;
10805 }
10806
10807 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mop, AArch64CG::kMd[mop]);
10808 vInsn.AddOpndChain(*res).AddOpndChain(*src);
10809 vInsn.PushRegSpecEntry(vecSpecSrc);
10810 GetCurBB()->AppendInsn(vInsn);
10811 return res;
10812 }
10813
10814 /* adalp o1, o2 instruction accumulates into o1, overwriting the original operand.
10815 Hence we perform c = vadalp(a,b) as
10816 T tmp = a;
10817 return tmp+b;
10818 The return value of vadalp is then assigned to c, leaving value of a intact.
10819 */
SelectVectorPairwiseAdalp(Operand * src1,PrimType sty1,Operand * src2,PrimType sty2)10820 RegOperand *AArch64CGFunc::SelectVectorPairwiseAdalp(Operand *src1, PrimType sty1, Operand *src2, PrimType sty2)
10821 {
10822 VectorRegSpec *vecSpecDest;
10823 RegOperand *res;
10824
10825 if (!IsPrimitiveVector(sty1)) {
10826 RegOperand *resF = SelectOneElementVectorCopy(src1, sty1);
10827 res = &CreateRegisterOperandOfType(PTY_f64);
10828 SelectCopy(*res, PTY_f64, *resF, PTY_f64);
10829 vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(k1ByteSize, k64BitSize);
10830 } else {
10831 res = &CreateRegisterOperandOfType(sty1); /* result type same as sty1 */
10832 SelectCopy(*res, sty1, *src1, sty1);
10833 vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(sty1);
10834 }
10835 VectorRegSpec *vecSpecSrc = GetMemoryPool()->New<VectorRegSpec>(sty2);
10836
10837 MOperator mop;
10838 if (IsUnsignedInteger(sty1)) {
10839 mop = GetPrimTypeSize(sty1) > k8ByteSize ? MOP_vupadalvv : MOP_vupadaluu;
10840 } else {
10841 mop = GetPrimTypeSize(sty1) > k8ByteSize ? MOP_vspadalvv : MOP_vspadaluu;
10842 }
10843
10844 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mop, AArch64CG::kMd[mop]);
10845 vInsn.AddOpndChain(*res).AddOpndChain(*src2);
10846 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpecSrc);
10847 GetCurBB()->AppendInsn(vInsn);
10848 if (!IsPrimitiveVector(sty1)) {
10849 res = AdjustOneElementVectorOperand(sty1, res);
10850 }
10851 return res;
10852 }
10853
SelectVectorPairwiseAdd(PrimType rType,Operand * src,PrimType sType)10854 RegOperand *AArch64CGFunc::SelectVectorPairwiseAdd(PrimType rType, Operand *src, PrimType sType)
10855 {
10856 PrimType oType = rType;
10857 rType = FilterOneElementVectorType(oType);
10858 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
10859 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
10860 VectorRegSpec *vecSpecSrc = GetMemoryPool()->New<VectorRegSpec>(sType); /* source operand */
10861
10862 if (rType == PTY_f64) {
10863 vecSpecDest->vecLaneMax = 1;
10864 }
10865
10866 MOperator mop;
10867 if (IsUnsignedInteger(sType)) {
10868 mop = GetPrimTypeSize(sType) > k8ByteSize ? MOP_vupaddvv : MOP_vupadduu;
10869 } else {
10870 mop = GetPrimTypeSize(sType) > k8ByteSize ? MOP_vspaddvv : MOP_vspadduu;
10871 }
10872
10873 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mop, AArch64CG::kMd[mop]);
10874 vInsn.AddOpndChain(*res).AddOpndChain(*src);
10875 /* dest pushed first, popped first */
10876 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpecSrc);
10877 GetCurBB()->AppendInsn(vInsn);
10878 if (oType != rType) {
10879 res = AdjustOneElementVectorOperand(oType, res);
10880 }
10881 return res;
10882 }
10883
SelectVectorSetElement(Operand * eOpnd,PrimType eType,Operand * vOpnd,PrimType vType,int32 lane)10884 RegOperand *AArch64CGFunc::SelectVectorSetElement(Operand *eOpnd, PrimType eType, Operand *vOpnd, PrimType vType,
10885 int32 lane)
10886 {
10887 if (!IsPrimitiveVector(vType)) {
10888 return SelectOneElementVectorCopy(eOpnd, eType);
10889 }
10890 RegOperand *reg = &CreateRegisterOperandOfType(eType); /* vector element type */
10891 SelectCopy(*reg, eType, *eOpnd, eType);
10892 VectorRegSpec *vecSpecSrc = GetMemoryPool()->New<VectorRegSpec>(vType, lane); /* vector operand == result */
10893
10894 MOperator mOp;
10895 if (GetPrimTypeSize(eType) > k4ByteSize) {
10896 mOp = GetPrimTypeSize(vType) > k8ByteSize ? MOP_vxinsvr : MOP_vxinsur;
10897 } else {
10898 mOp = GetPrimTypeSize(vType) > k8ByteSize ? MOP_vwinsvr : MOP_vwinsur;
10899 }
10900
10901 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
10902 vInsn.AddOpndChain(*vOpnd).AddOpndChain(*reg);
10903 vInsn.PushRegSpecEntry(vecSpecSrc);
10904 GetCurBB()->AppendInsn(vInsn);
10905 return static_cast<RegOperand *>(vOpnd);
10906 }
10907
SelectVectorAbsSubL(PrimType rType,Operand * o1,Operand * o2,PrimType oTy,bool isLow)10908 RegOperand *AArch64CGFunc::SelectVectorAbsSubL(PrimType rType, Operand *o1, Operand *o2, PrimType oTy, bool isLow)
10909 {
10910 RegOperand *res = &CreateRegisterOperandOfType(rType);
10911 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
10912 VectorRegSpec *vecSpecOpd1 = GetMemoryPool()->New<VectorRegSpec>(oTy);
10913 VectorRegSpec *vecSpecOpd2 = GetMemoryPool()->New<VectorRegSpec>(oTy); /* same opnd types */
10914
10915 MOperator mop;
10916 if (isLow) {
10917 mop = IsPrimitiveUnSignedVector(rType) ? MOP_vuabdlvuu : MOP_vsabdlvuu;
10918 } else {
10919 mop = IsPrimitiveUnSignedVector(rType) ? MOP_vuabdl2vvv : MOP_vsabdl2vvv;
10920 }
10921 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mop, AArch64CG::kMd[mop]);
10922 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*o2);
10923 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpecOpd1).PushRegSpecEntry(vecSpecOpd2);
10924 GetCurBB()->AppendInsn(vInsn);
10925 return res;
10926 }
10927
SelectVectorMerge(PrimType rType,Operand * o1,Operand * o2,int32 index)10928 RegOperand *AArch64CGFunc::SelectVectorMerge(PrimType rType, Operand *o1, Operand *o2, int32 index)
10929 {
10930 if (!IsPrimitiveVector(rType)) {
10931 static_cast<RegOperand *>(o1)->SetIF64Vec();
10932 return static_cast<RegOperand *>(o1); /* 64x1_t, index equals 0 */
10933 }
10934 RegOperand *res = &CreateRegisterOperandOfType(rType);
10935 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
10936 VectorRegSpec *vecSpecOpd1 = GetMemoryPool()->New<VectorRegSpec>(rType);
10937 VectorRegSpec *vecSpecOpd2 = GetMemoryPool()->New<VectorRegSpec>(rType);
10938
10939 ImmOperand *imm = &CreateImmOperand(index, k8BitSize, true);
10940
10941 MOperator mOp = (GetPrimTypeSize(rType) > k8ByteSize) ? MOP_vextvvvi : MOP_vextuuui;
10942 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
10943 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*o2).AddOpndChain(*imm);
10944 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpecOpd1).PushRegSpecEntry(vecSpecOpd2);
10945 GetCurBB()->AppendInsn(vInsn);
10946 return res;
10947 }
10948
SelectVectorReverse(PrimType rType,Operand * src,PrimType sType,uint32 size)10949 RegOperand *AArch64CGFunc::SelectVectorReverse(PrimType rType, Operand *src, PrimType sType, uint32 size)
10950 {
10951 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
10952 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
10953 VectorRegSpec *vecSpecSrc = GetMemoryPool()->New<VectorRegSpec>(sType); /* vector operand */
10954
10955 MOperator mOp;
10956 if (GetPrimTypeBitSize(rType) == k128BitSize) {
10957 mOp = size >= k64BitSize ? MOP_vrev64qq : (size >= k32BitSize ? MOP_vrev32qq : MOP_vrev16qq);
10958 } else if (GetPrimTypeBitSize(rType) == k64BitSize) {
10959 mOp = size >= k64BitSize ? MOP_vrev64dd : (size >= k32BitSize ? MOP_vrev32dd : MOP_vrev16dd);
10960 } else {
10961 CHECK_FATAL(false, "should not be here");
10962 }
10963 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
10964 vInsn.AddOpndChain(*res).AddOpndChain(*src);
10965 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpecSrc);
10966 GetCurBB()->AppendInsn(vInsn);
10967 return res;
10968 }
10969
SelectVectorSum(PrimType rType,Operand * o1,PrimType oType)10970 RegOperand *AArch64CGFunc::SelectVectorSum(PrimType rType, Operand *o1, PrimType oType)
10971 {
10972 RegOperand *res = &CreateRegisterOperandOfType(rType); /* uint32_t result */
10973 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(oType);
10974 RegOperand *iOpnd = &CreateRegisterOperandOfType(oType); /* float intermediate result */
10975 uint32 eSize = GetVecEleSize(oType); /* vector opd in bits */
10976 bool is16ByteVec = GetPrimTypeSize(oType) >= k16ByteSize;
10977 MOperator mOp;
10978 if (is16ByteVec) {
10979 mOp = eSize <= k8BitSize
10980 ? MOP_vbaddvrv
10981 : (eSize <= k16BitSize ? MOP_vhaddvrv : (eSize <= k32BitSize ? MOP_vsaddvrv : MOP_vdaddvrv));
10982 } else {
10983 mOp = eSize <= k8BitSize ? MOP_vbaddvru : (eSize <= k16BitSize ? MOP_vhaddvru : MOP_vsaddvru);
10984 }
10985 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
10986 vInsn.AddOpndChain(*iOpnd).AddOpndChain(*o1);
10987 vInsn.PushRegSpecEntry(vecSpec1);
10988 GetCurBB()->AppendInsn(vInsn);
10989
10990 mOp = eSize > k32BitSize ? MOP_vxmovrv : MOP_vwmovrv;
10991 VectorInsn &vInsn2 = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
10992 auto *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(oType);
10993 vInsn2.AddOpndChain(*res).AddOpndChain(*iOpnd);
10994 vecSpec2->vecLane = 0;
10995 vInsn2.PushRegSpecEntry(vecSpec2);
10996 GetCurBB()->AppendInsn(vInsn2);
10997 return res;
10998 }
10999
PrepareVectorOperands(Operand ** o1,PrimType & oty1,Operand ** o2,PrimType & oty2)11000 void AArch64CGFunc::PrepareVectorOperands(Operand **o1, PrimType &oty1, Operand **o2, PrimType &oty2)
11001 {
11002 /* Only 1 operand can be non vector, otherwise it's a scalar operation, wouldn't come here */
11003 if (IsPrimitiveVector(oty1) == IsPrimitiveVector(oty2)) {
11004 return;
11005 }
11006 PrimType origTyp = !IsPrimitiveVector(oty2) ? oty2 : oty1;
11007 Operand *opd = !IsPrimitiveVector(oty2) ? *o2 : *o1;
11008 PrimType rType = !IsPrimitiveVector(oty2) ? oty1 : oty2; /* Type to dup into */
11009 RegOperand *res = &CreateRegisterOperandOfType(rType);
11010 VectorRegSpec *vecSpec = GetMemoryPool()->New<VectorRegSpec>(rType);
11011
11012 bool immOpnd = false;
11013 if (opd->IsConstImmediate()) {
11014 int64 val = static_cast<ImmOperand *>(opd)->GetValue();
11015 if (val >= kMinImmVal && val <= kMaxImmVal && GetVecEleSize(rType) < k64BitSize) {
11016 immOpnd = true;
11017 } else {
11018 RegOperand *regOpd = &CreateRegisterOperandOfType(origTyp);
11019 SelectCopyImm(*regOpd, origTyp, static_cast<ImmOperand &>(*opd), origTyp);
11020 opd = static_cast<Operand *>(regOpd);
11021 }
11022 }
11023
11024 /* need dup to vector operand */
11025 MOperator mOp;
11026 if (immOpnd) {
11027 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vmovvi : MOP_vmovui; /* a const */
11028 } else {
11029 if (GetPrimTypeSize(origTyp) > k4ByteSize) {
11030 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vxdupvr : MOP_vxdupur;
11031 } else {
11032 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vwdupvr : MOP_vwdupur; /* a scalar var */
11033 }
11034 }
11035 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11036 vInsn.AddOpndChain(*res).AddOpndChain(*opd);
11037 vInsn.PushRegSpecEntry(vecSpec);
11038 GetCurBB()->AppendInsn(vInsn);
11039 if (!IsPrimitiveVector(oty2)) {
11040 *o2 = static_cast<Operand *>(res);
11041 oty2 = rType;
11042 } else {
11043 *o1 = static_cast<Operand *>(res);
11044 oty1 = rType;
11045 }
11046 }
11047
SelectVectorCvt(Operand * res,PrimType rType,Operand * o1,PrimType oType)11048 void AArch64CGFunc::SelectVectorCvt(Operand *res, PrimType rType, Operand *o1, PrimType oType)
11049 {
11050 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11051 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(oType); /* vector operand 1 */
11052
11053 MOperator mOp;
11054 VectorInsn *insn;
11055 if (GetPrimTypeSize(rType) > GetPrimTypeSize(oType)) {
11056 /* expand, similar to vmov_XX() intrinsics */
11057 mOp = IsUnsignedInteger(rType) ? MOP_vushllvvi : MOP_vshllvvi;
11058 ImmOperand *imm = &CreateImmOperand(0, k8BitSize, true);
11059 insn = &GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11060 insn->AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*imm);
11061 } else if (GetPrimTypeSize(rType) < GetPrimTypeSize(oType)) {
11062 /* extract, similar to vqmovn_XX() intrinsics */
11063 insn = &GetInsnBuilder()->BuildVectorInsn(MOP_vxtnuv, AArch64CG::kMd[MOP_vxtnuv]);
11064 insn->AddOpndChain(*res).AddOpndChain(*o1);
11065 } else {
11066 CHECK_FATAL(0, "Invalid cvt between 2 operands of the same size");
11067 }
11068 insn->PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1);
11069 GetCurBB()->AppendInsn(*insn);
11070 }
11071
SelectVectorCompareZero(Operand * o1,PrimType oty1,Operand * o2,Opcode opc)11072 RegOperand *AArch64CGFunc::SelectVectorCompareZero(Operand *o1, PrimType oty1, Operand *o2, Opcode opc)
11073 {
11074 if (IsUnsignedInteger(oty1) && (opc != OP_eq && opc != OP_ne)) {
11075 return nullptr; /* no unsigned instr for zero */
11076 }
11077 RegOperand *res = &CreateRegisterOperandOfType(oty1); /* result operand */
11078 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(oty1);
11079 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(oty1); /* vector operand 1 */
11080
11081 MOperator mOp;
11082 switch (opc) {
11083 case OP_eq:
11084 case OP_ne:
11085 mOp = GetPrimTypeSize(oty1) > k8ByteSize ? MOP_vzcmeqvv : MOP_vzcmequu;
11086 break;
11087 case OP_gt:
11088 mOp = GetPrimTypeSize(oty1) > k8ByteSize ? MOP_vzcmgtvv : MOP_vzcmgtuu;
11089 break;
11090 case OP_ge:
11091 mOp = GetPrimTypeSize(oty1) > k8ByteSize ? MOP_vzcmgevv : MOP_vzcmgeuu;
11092 break;
11093 case OP_lt:
11094 mOp = GetPrimTypeSize(oty1) > k8ByteSize ? MOP_vzcmltvv : MOP_vzcmltuu;
11095 break;
11096 case OP_le:
11097 mOp = GetPrimTypeSize(oty1) > k8ByteSize ? MOP_vzcmlevv : MOP_vzcmleuu;
11098 break;
11099 default:
11100 CHECK_FATAL(0, "Invalid cc in vector compare");
11101 }
11102 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11103 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*o2);
11104 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1);
11105 GetCurBB()->AppendInsn(vInsn);
11106 if (opc == OP_ne) {
11107 res = SelectVectorNot(oty1, res);
11108 }
11109 return res;
11110 }
11111
11112 /* Neon compare intrinsics always return unsigned vector, MapleIR for comparison always return
11113 signed. Using type of 1st operand for operation here */
SelectVectorCompare(Operand * o1,PrimType oty1,Operand * o2,PrimType oty2,Opcode opc)11114 RegOperand *AArch64CGFunc::SelectVectorCompare(Operand *o1, PrimType oty1, Operand *o2, PrimType oty2, Opcode opc)
11115 {
11116 if (o2->IsConstImmediate() && static_cast<ImmOperand *>(o2)->GetValue() == 0) {
11117 RegOperand *zeroCmp = SelectVectorCompareZero(o1, oty1, o2, opc);
11118 if (zeroCmp != nullptr) {
11119 return zeroCmp;
11120 }
11121 }
11122 PrepareVectorOperands(&o1, oty1, &o2, oty2);
11123 DEBUG_ASSERT(oty1 == oty2, "vector operand type mismatch");
11124
11125 RegOperand *res = &CreateRegisterOperandOfType(oty1); /* result operand */
11126 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(oty1);
11127 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(oty1); /* vector operand 1 */
11128 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(oty2); /* vector operand 2 */
11129
11130 MOperator mOp;
11131 switch (opc) {
11132 case OP_eq:
11133 case OP_ne:
11134 mOp = GetPrimTypeSize(oty1) > k8ByteSize ? MOP_vcmeqvvv : MOP_vcmequuu;
11135 break;
11136 case OP_lt:
11137 case OP_gt:
11138 if (IsUnsignedInteger(oty1)) {
11139 mOp = GetPrimTypeSize(oty1) > k8ByteSize ? MOP_vcmhivvv : MOP_vcmhiuuu;
11140 } else {
11141 mOp = GetPrimTypeSize(oty1) > k8ByteSize ? MOP_vcmgtvvv : MOP_vcmgtuuu;
11142 }
11143 break;
11144 case OP_le:
11145 case OP_ge:
11146 if (IsUnsignedInteger(oty1)) {
11147 mOp = GetPrimTypeSize(oty1) > k8ByteSize ? MOP_vcmhsvvv : MOP_vcmhsuuu;
11148 } else {
11149 mOp = GetPrimTypeSize(oty1) > k8ByteSize ? MOP_vcmgevvv : MOP_vcmgeuuu;
11150 }
11151 break;
11152 default:
11153 CHECK_FATAL(0, "Invalid cc in vector compare");
11154 }
11155 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11156 if (opc == OP_lt || opc == OP_le) {
11157 vInsn.AddOpndChain(*res).AddOpndChain(*o2).AddOpndChain(*o1);
11158 } else {
11159 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*o2);
11160 }
11161 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1).PushRegSpecEntry(vecSpec2);
11162 GetCurBB()->AppendInsn(vInsn);
11163 if (opc == OP_ne) {
11164 res = SelectVectorNot(oty1, res);
11165 }
11166 return res;
11167 }
11168
SelectVectorShift(PrimType rType,Operand * o1,PrimType oty1,Operand * o2,PrimType oty2,Opcode opc)11169 RegOperand *AArch64CGFunc::SelectVectorShift(PrimType rType, Operand *o1, PrimType oty1, Operand *o2, PrimType oty2,
11170 Opcode opc)
11171 {
11172 PrepareVectorOperands(&o1, oty1, &o2, oty2);
11173 PrimType resultType = rType;
11174 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11175 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(rType); /* vector operand 1 */
11176 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(rType); /* vector operand 2 */
11177
11178 if (!IsPrimitiveVector(rType)) {
11179 o1 = &SelectCopy(*o1, rType, PTY_f64);
11180 o2 = &SelectCopy(*o2, rType, PTY_f64);
11181 resultType = PTY_f64;
11182 }
11183 RegOperand *res = &CreateRegisterOperandOfType(resultType); /* result operand */
11184
11185 /* signed and unsigned shl(v,v) both use sshl or ushl, they are the same */
11186 MOperator mOp;
11187 if (IsPrimitiveUnsigned(rType)) {
11188 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vushlvvv : MOP_vushluuu;
11189 } else {
11190 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vshlvvv : MOP_vshluuu;
11191 }
11192
11193 if (opc != OP_shl) {
11194 o2 = SelectVectorNeg(rType, o2);
11195 }
11196 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11197 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*o2);
11198 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1).PushRegSpecEntry(vecSpec2);
11199 GetCurBB()->AppendInsn(vInsn);
11200 return res;
11201 }
11202
ValidShiftConst(PrimType rType)11203 uint32 ValidShiftConst(PrimType rType)
11204 {
11205 switch (rType) {
11206 case PTY_v8u8:
11207 case PTY_v8i8:
11208 case PTY_v16u8:
11209 case PTY_v16i8:
11210 return k8BitSize;
11211 case PTY_v4u16:
11212 case PTY_v4i16:
11213 case PTY_v8u16:
11214 case PTY_v8i16:
11215 return k16BitSize;
11216 case PTY_v2u32:
11217 case PTY_v2i32:
11218 case PTY_v4u32:
11219 case PTY_v4i32:
11220 return k32BitSize;
11221 case PTY_v2u64:
11222 case PTY_v2i64:
11223 return k64BitSize;
11224 default:
11225 CHECK_FATAL(0, "Invalid Shift operand type");
11226 }
11227 return 0;
11228 }
11229
SelectVectorShiftImm(PrimType rType,Operand * o1,Operand * imm,int32 sVal,Opcode opc)11230 RegOperand *AArch64CGFunc::SelectVectorShiftImm(PrimType rType, Operand *o1, Operand *imm, int32 sVal, Opcode opc)
11231 {
11232 auto resultType = FilterOneElementVectorType(rType);
11233 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
11234 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11235 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(rType); /* vector operand 1 */
11236
11237 if (!imm->IsConstImmediate()) {
11238 CHECK_FATAL(0, "VectorUShiftImm has invalid shift const");
11239 }
11240 uint32 shift = static_cast<uint32>(ValidShiftConst(rType));
11241 bool needDup = false;
11242 if (opc == OP_shl) {
11243 if ((shift == k8BitSize && (sVal < 0 || static_cast<uint32>(sVal) >= shift)) ||
11244 (shift == k16BitSize && (sVal < 0 || static_cast<uint32>(sVal) >= shift)) ||
11245 (shift == k32BitSize && (sVal < 0 || static_cast<uint32>(sVal) >= shift)) ||
11246 (shift == k64BitSize && (sVal < 0 || static_cast<uint32>(sVal) >= shift))) {
11247 needDup = true;
11248 }
11249 } else {
11250 if ((shift == k8BitSize && (sVal < 1 || static_cast<uint32>(sVal) > shift)) ||
11251 (shift == k16BitSize && (sVal < 1 || static_cast<uint32>(sVal) > shift)) ||
11252 (shift == k32BitSize && (sVal < 1 || static_cast<uint32>(sVal) > shift)) ||
11253 (shift == k64BitSize && (sVal < 1 || static_cast<uint32>(sVal) > shift))) {
11254 needDup = true;
11255 }
11256 }
11257 if (needDup) {
11258 /* Dup constant to vector reg */
11259 SelectCopy(*res, resultType, *imm, imm->GetSize() == k64BitSize ? PTY_u64 : PTY_u32);
11260 res = SelectVectorShift(rType, o1, rType, res, rType, opc);
11261 return res;
11262 }
11263 MOperator mOp;
11264 if (GetPrimTypeSize(rType) > k8ByteSize) {
11265 if (IsUnsignedInteger(rType)) {
11266 mOp = opc == OP_shl ? MOP_vushlvvi : MOP_vushrvvi;
11267 } else {
11268 mOp = opc == OP_shl ? MOP_vushlvvi : MOP_vshrvvi;
11269 }
11270 } else {
11271 if (IsUnsignedInteger(rType)) {
11272 mOp = opc == OP_shl ? MOP_vushluui : MOP_vushruui;
11273 } else {
11274 mOp = opc == OP_shl ? MOP_vushluui : MOP_vshruui;
11275 }
11276 }
11277 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11278 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*imm);
11279 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1);
11280 GetCurBB()->AppendInsn(vInsn);
11281 return res;
11282 }
11283
SelectVectorTableLookup(PrimType rType,Operand * o1,Operand * o2)11284 RegOperand *AArch64CGFunc::SelectVectorTableLookup(PrimType rType, Operand *o1, Operand *o2)
11285 {
11286 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
11287 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType); /* 8B or 16B */
11288 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(rType); /* vector operand 1 */
11289 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(rType); /* vector operand 2 */
11290 vecSpec1->compositeOpnds = 1; /* composite operand */
11291
11292 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(MOP_vtbl1vvv, AArch64CG::kMd[MOP_vtbl1vvv]);
11293 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*o2);
11294 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1).PushRegSpecEntry(vecSpec2);
11295 GetCurBB()->AppendInsn(vInsn);
11296 return res;
11297 }
11298
SelectVectorMadd(Operand * o1,PrimType oTyp1,Operand * o2,PrimType oTyp2,Operand * o3,PrimType oTyp3)11299 RegOperand *AArch64CGFunc::SelectVectorMadd(Operand *o1, PrimType oTyp1, Operand *o2, PrimType oTyp2, Operand *o3,
11300 PrimType oTyp3)
11301 {
11302 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(oTyp1); /* operand 1 and result */
11303 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(oTyp2); /* vector operand 2 */
11304 VectorRegSpec *vecSpec3 = GetMemoryPool()->New<VectorRegSpec>(oTyp3); /* vector operand 2 */
11305
11306 MOperator mop = IsPrimitiveUnSignedVector(oTyp1) ? MOP_vumaddvvv : MOP_vsmaddvvv;
11307 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mop, AArch64CG::kMd[mop]);
11308 vInsn.AddOpndChain(*o1).AddOpndChain(*o2).AddOpndChain(*o3);
11309 vInsn.PushRegSpecEntry(vecSpec1).PushRegSpecEntry(vecSpec2).PushRegSpecEntry(vecSpec3);
11310 GetCurBB()->AppendInsn(vInsn);
11311 return static_cast<RegOperand *>(o1);
11312 }
11313
SelectVectorMull(PrimType rType,Operand * o1,PrimType oTyp1,Operand * o2,PrimType oTyp2,bool isLow)11314 RegOperand *AArch64CGFunc::SelectVectorMull(PrimType rType, Operand *o1, PrimType oTyp1, Operand *o2, PrimType oTyp2,
11315 bool isLow)
11316 {
11317 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
11318 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11319 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(oTyp1); /* vector operand 1 */
11320 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(oTyp2); /* vector operand 1 */
11321
11322 MOperator mop;
11323 if (isLow) {
11324 mop = IsPrimitiveUnSignedVector(rType) ? MOP_vumullvvv : MOP_vsmullvvv;
11325 } else {
11326 mop = IsPrimitiveUnSignedVector(rType) ? MOP_vumull2vvv : MOP_vsmull2vvv;
11327 }
11328 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mop, AArch64CG::kMd[mop]);
11329 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*o2);
11330 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1).PushRegSpecEntry(vecSpec2);
11331 GetCurBB()->AppendInsn(vInsn);
11332 return res;
11333 }
11334
SelectVectorBinOp(PrimType rType,Operand * o1,PrimType oty1,Operand * o2,PrimType oty2,Opcode opc)11335 RegOperand *AArch64CGFunc::SelectVectorBinOp(PrimType rType, Operand *o1, PrimType oty1, Operand *o2, PrimType oty2,
11336 Opcode opc)
11337 {
11338 PrepareVectorOperands(&o1, oty1, &o2, oty2);
11339 DEBUG_ASSERT(oty1 == oty2, "vector operand type mismatch");
11340
11341 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
11342 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11343 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(oty1); /* source operand 1 */
11344 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(oty2); /* source operand 2 */
11345
11346 MOperator mOp;
11347 if (opc == OP_add) {
11348 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vaddvvv : MOP_vadduuu;
11349 } else if (opc == OP_sub) {
11350 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vsubvvv : MOP_vsubuuu;
11351 } else if (opc == OP_mul) {
11352 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vmulvvv : MOP_vmuluuu;
11353 } else {
11354 CHECK_FATAL(0, "Invalid opcode for SelectVectorBinOp");
11355 }
11356 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11357 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*o2);
11358 /* dest pushed first, popped first */
11359 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1).PushRegSpecEntry(vecSpec2);
11360 GetCurBB()->AppendInsn(vInsn);
11361 return res;
11362 }
11363
SelectVectorBitwiseOp(PrimType rType,Operand * o1,PrimType oty1,Operand * o2,PrimType oty2,Opcode opc)11364 RegOperand *AArch64CGFunc::SelectVectorBitwiseOp(PrimType rType, Operand *o1, PrimType oty1, Operand *o2, PrimType oty2,
11365 Opcode opc)
11366 {
11367 PrepareVectorOperands(&o1, oty1, &o2, oty2);
11368 DEBUG_ASSERT(oty1 == oty2, "vector operand type mismatch");
11369
11370 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
11371 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11372 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(rType); /* vector operand 1 */
11373 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(rType); /* vector operand 1 */
11374
11375 MOperator mOp;
11376 if (opc == OP_band) {
11377 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vandvvv : MOP_vanduuu;
11378 } else if (opc == OP_bior) {
11379 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vorvvv : MOP_voruuu;
11380 } else if (opc == OP_bxor) {
11381 mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vxorvvv : MOP_vxoruuu;
11382 } else {
11383 CHECK_FATAL(0, "Invalid opcode for SelectVectorBitwiseOp");
11384 }
11385 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11386 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*o2);
11387 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1).PushRegSpecEntry(vecSpec2);
11388 GetCurBB()->AppendInsn(vInsn);
11389 return res;
11390 }
11391
SelectVectorNarrow(PrimType rType,Operand * o1,PrimType otyp)11392 RegOperand *AArch64CGFunc::SelectVectorNarrow(PrimType rType, Operand *o1, PrimType otyp)
11393 {
11394 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
11395 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11396 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(otyp); /* vector operand */
11397
11398 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(MOP_vxtnuv, AArch64CG::kMd[MOP_vxtnuv]);
11399 vInsn.AddOpndChain(*res).AddOpndChain(*o1);
11400 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1);
11401 GetCurBB()->AppendInsn(vInsn);
11402 return res;
11403 }
11404
SelectVectorNarrow2(PrimType rType,Operand * o1,PrimType oty1,Operand * o2,PrimType oty2)11405 RegOperand *AArch64CGFunc::SelectVectorNarrow2(PrimType rType, Operand *o1, PrimType oty1, Operand *o2, PrimType oty2)
11406 {
11407 (void)oty1; /* 1st opnd was loaded already, type no longer needed */
11408 RegOperand *res = static_cast<RegOperand *>(o1); /* o1 is also the result */
11409 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11410 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(oty2); /* vector opnd2 */
11411
11412 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(MOP_vxtn2uv, AArch64CG::kMd[MOP_vxtn2uv]);
11413 vInsn.AddOpndChain(*res).AddOpndChain(*o2);
11414 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec2);
11415 GetCurBB()->AppendInsn(vInsn);
11416 return res;
11417 }
11418
SelectVectorNot(PrimType rType,Operand * o1)11419 RegOperand *AArch64CGFunc::SelectVectorNot(PrimType rType, Operand *o1)
11420 {
11421 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
11422 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11423 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(rType); /* vector operand 1 */
11424
11425 MOperator mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vnotvv : MOP_vnotuu;
11426 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11427 vInsn.AddOpndChain(*res).AddOpndChain(*o1);
11428 vInsn.PushRegSpecEntry(vecSpecDest).PushRegSpecEntry(vecSpec1);
11429 GetCurBB()->AppendInsn(vInsn);
11430 return res;
11431 }
11432
SelectVectorNeg(PrimType rType,Operand * o1)11433 RegOperand *AArch64CGFunc::SelectVectorNeg(PrimType rType, Operand *o1)
11434 {
11435 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
11436 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11437 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(rType); /* vector operand 1 */
11438
11439 MOperator mOp = GetPrimTypeSize(rType) > k8ByteSize ? MOP_vnegvv : MOP_vneguu;
11440 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11441 vInsn.AddOpndChain(*res).AddOpndChain(*o1);
11442 vInsn.PushRegSpecEntry(vecSpecDest);
11443 vInsn.PushRegSpecEntry(vecSpec1);
11444 GetCurBB()->AppendInsn(vInsn);
11445 return res;
11446 }
11447
11448 /*
11449 * Called internally for auto-vec, no intrinsics for now
11450 */
SelectVectorSelect(Operand & cond,PrimType rType,Operand & o0,Operand & o1)11451 RegOperand *AArch64CGFunc::SelectVectorSelect(Operand &cond, PrimType rType, Operand &o0, Operand &o1)
11452 {
11453 rType = GetPrimTypeSize(rType) > k8ByteSize ? PTY_v16u8 : PTY_v8u8;
11454 RegOperand *res = &CreateRegisterOperandOfType(rType);
11455 SelectCopy(*res, rType, cond, rType);
11456 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11457 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(rType);
11458 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(rType);
11459
11460 uint32 mOp = GetPrimTypeBitSize(rType) > k64BitSize ? MOP_vbslvvv : MOP_vbsluuu;
11461 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11462 vInsn.AddOpndChain(*res).AddOpndChain(o0).AddOpndChain(o1);
11463 vInsn.PushRegSpecEntry(vecSpecDest);
11464 vInsn.PushRegSpecEntry(vecSpec1);
11465 vInsn.PushRegSpecEntry(vecSpec2);
11466 GetCurBB()->AppendInsn(vInsn);
11467 return res;
11468 }
11469
SelectVectorShiftRNarrow(PrimType rType,Operand * o1,PrimType oType,Operand * o2,bool isLow)11470 RegOperand *AArch64CGFunc::SelectVectorShiftRNarrow(PrimType rType, Operand *o1, PrimType oType, Operand *o2,
11471 bool isLow)
11472 {
11473 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
11474 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11475 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(oType); /* vector operand 1 */
11476
11477 ImmOperand *imm = static_cast<ImmOperand *>(o2);
11478 MOperator mOp;
11479 if (isLow) {
11480 mOp = MOP_vshrnuvi;
11481 } else {
11482 CHECK_FATAL(0, "NYI: vshrn_high_");
11483 }
11484 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11485 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*imm);
11486 vInsn.PushRegSpecEntry(vecSpecDest);
11487 vInsn.PushRegSpecEntry(vecSpec1);
11488 GetCurBB()->AppendInsn(vInsn);
11489 return res;
11490 }
11491
SelectVectorSubWiden(PrimType resType,Operand * o1,PrimType otyp1,Operand * o2,PrimType otyp2,bool isLow,bool isWide)11492 RegOperand *AArch64CGFunc::SelectVectorSubWiden(PrimType resType, Operand *o1, PrimType otyp1, Operand *o2,
11493 PrimType otyp2, bool isLow, bool isWide)
11494 {
11495 RegOperand *res = &CreateRegisterOperandOfType(resType); /* result reg */
11496 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(resType);
11497 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(otyp1); /* vector operand 1 */
11498 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(otyp2); /* vector operand 2 */
11499
11500 MOperator mOp;
11501 if (!isWide) {
11502 if (isLow) {
11503 mOp = IsUnsignedInteger(otyp1) ? MOP_vusublvuu : MOP_vssublvuu;
11504 } else {
11505 mOp = IsUnsignedInteger(otyp1) ? MOP_vusubl2vvv : MOP_vssubl2vvv;
11506 }
11507 } else {
11508 if (isLow) {
11509 mOp = IsUnsignedInteger(otyp1) ? MOP_vusubwvvu : MOP_vssubwvvu;
11510 } else {
11511 mOp = IsUnsignedInteger(otyp1) ? MOP_vusubw2vvv : MOP_vssubw2vvv;
11512 }
11513 }
11514 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11515 vInsn.AddOpndChain(*res).AddOpndChain(*o1).AddOpndChain(*o2);
11516 vInsn.PushRegSpecEntry(vecSpecDest);
11517 vInsn.PushRegSpecEntry(vecSpec1);
11518 vInsn.PushRegSpecEntry(vecSpec2);
11519 GetCurBB()->AppendInsn(vInsn);
11520 return res;
11521 }
11522
SelectVectorZip(PrimType rType,Operand * o1,Operand * o2)11523 void AArch64CGFunc::SelectVectorZip(PrimType rType, Operand *o1, Operand *o2)
11524 {
11525 RegOperand *res1 = &CreateRegisterOperandOfType(rType); /* result operand 1 */
11526 RegOperand *res2 = &CreateRegisterOperandOfType(rType); /* result operand 2 */
11527 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11528 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(rType); /* vector operand 1 */
11529 VectorRegSpec *vecSpec2 = GetMemoryPool()->New<VectorRegSpec>(rType); /* vector operand 2 */
11530
11531 VectorInsn &vInsn1 = GetInsnBuilder()->BuildVectorInsn(MOP_vzip1vvv, AArch64CG::kMd[MOP_vzip1vvv]);
11532 vInsn1.AddOpndChain(*res1).AddOpndChain(*o1).AddOpndChain(*o2);
11533 vInsn1.PushRegSpecEntry(vecSpecDest);
11534 vInsn1.PushRegSpecEntry(vecSpec1);
11535 vInsn1.PushRegSpecEntry(vecSpec2);
11536 GetCurBB()->AppendInsn(vInsn1);
11537
11538 VectorInsn &vInsn2 = GetInsnBuilder()->BuildVectorInsn(MOP_vzip2vvv, AArch64CG::kMd[MOP_vzip2vvv]);
11539 vInsn2.AddOpndChain(*res2).AddOpndChain(*o1).AddOpndChain(*o2);
11540 vInsn2.PushRegSpecEntry(vecSpecDest);
11541 vInsn2.PushRegSpecEntry(vecSpec1);
11542 vInsn2.PushRegSpecEntry(vecSpec2);
11543 GetCurBB()->AppendInsn(vInsn2);
11544
11545 if (GetPrimTypeSize(rType) <= k16ByteSize) {
11546 Operand *preg1 = &GetOrCreatePhysicalRegisterOperand(V0, k64BitSize, kRegTyFloat);
11547 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xvmovd, *preg1, *res1));
11548 Operand *preg2 = &GetOrCreatePhysicalRegisterOperand(V1, k64BitSize, kRegTyFloat);
11549 GetCurBB()->AppendInsn(GetInsnBuilder()->BuildInsn(MOP_xvmovd, *preg2, *res2));
11550 }
11551 }
11552
SelectVectorWiden(PrimType rType,Operand * o1,PrimType otyp,bool isLow)11553 RegOperand *AArch64CGFunc::SelectVectorWiden(PrimType rType, Operand *o1, PrimType otyp, bool isLow)
11554 {
11555 RegOperand *res = &CreateRegisterOperandOfType(rType); /* result operand */
11556 VectorRegSpec *vecSpecDest = GetMemoryPool()->New<VectorRegSpec>(rType);
11557 VectorRegSpec *vecSpec1 = GetMemoryPool()->New<VectorRegSpec>(otyp); /* vector operand */
11558
11559 MOperator mOp;
11560 if (isLow) {
11561 mOp = IsPrimitiveUnSignedVector(rType) ? MOP_vuxtlvu : MOP_vsxtlvu;
11562 } else {
11563 mOp = IsPrimitiveUnSignedVector(rType) ? MOP_vuxtl2vv : MOP_vsxtl2vv;
11564 }
11565 VectorInsn &vInsn = GetInsnBuilder()->BuildVectorInsn(mOp, AArch64CG::kMd[mOp]);
11566 vInsn.AddOpndChain(*res).AddOpndChain(*o1);
11567 vInsn.PushRegSpecEntry(vecSpecDest);
11568 vInsn.PushRegSpecEntry(vecSpec1);
11569 GetCurBB()->AppendInsn(vInsn);
11570 return res;
11571 }
11572
11573 /* Check the distance between the first insn of BB with the lable(targ_labidx)
11574 * and the insn with targ_id. If the distance greater than maxDistance
11575 * return false.
11576 */
DistanceCheck(const BB & bb,LabelIdx targLabIdx,uint32 targId,uint32 maxDistance) const11577 bool AArch64CGFunc::DistanceCheck(const BB &bb, LabelIdx targLabIdx, uint32 targId, uint32 maxDistance) const
11578 {
11579 for (auto *tBB : bb.GetSuccs()) {
11580 if (tBB->GetLabIdx() != targLabIdx) {
11581 continue;
11582 }
11583 Insn *tInsn = tBB->GetFirstInsn();
11584 while (tInsn == nullptr || !tInsn->IsMachineInstruction()) {
11585 if (tInsn == nullptr) {
11586 tBB = tBB->GetNext();
11587 if (tBB == nullptr) { /* tailcallopt may make the target block empty */
11588 return true;
11589 }
11590 tInsn = tBB->GetFirstInsn();
11591 } else {
11592 tInsn = tInsn->GetNext();
11593 }
11594 }
11595 uint32 tmp = (tInsn->GetId() > targId) ? (tInsn->GetId() - targId) : (targId - tInsn->GetId());
11596 return (tmp < maxDistance);
11597 }
11598 CHECK_FATAL(false, "CFG error");
11599 }
11600
SplitInt128(Operand & opnd)11601 AArch64CGFunc::SplittedInt128 AArch64CGFunc::SplitInt128(Operand &opnd)
11602 {
11603 DEBUG_ASSERT(opnd.IsRegister(), "expected register opnd");
11604 auto vecTy = PTY_v2u64;
11605 auto scTy = PTY_u64;
11606 Operand &low = *SelectVectorGetElement(scTy, &opnd, vecTy, 0);
11607 Operand &high = *SelectVectorGetElement(scTy, &opnd, vecTy, 1);
11608 return {low, high};
11609 }
11610
CombineInt128(const SplittedInt128 parts)11611 RegOperand &AArch64CGFunc::CombineInt128(const SplittedInt128 parts)
11612 {
11613 RegOperand &resOpnd = CreateRegisterOperandOfType(PTY_v2u64);
11614 CombineInt128(resOpnd, parts);
11615 return resOpnd;
11616 }
11617
CombineInt128(Operand & resOpnd,const SplittedInt128 parts)11618 void AArch64CGFunc::CombineInt128(Operand &resOpnd, const SplittedInt128 parts)
11619 {
11620 auto vecTy = PTY_v2u64;
11621 auto scTy = PTY_u64;
11622 auto *tmpOpnd = SelectVectorFromScalar(vecTy, &parts.low, scTy);
11623 SelectCopy(resOpnd, vecTy, *SelectVectorSetElement(&parts.high, scTy, tmpOpnd, vecTy, 1), vecTy);
11624 }
11625
SelectParmListForInt128(Operand & opnd,ListOperand & srcOpnds,const CCLocInfo & ploc,bool isSpecialArg,std::vector<RegMapForPhyRegCpy> & regMapForTmpBB)11626 void AArch64CGFunc::SelectParmListForInt128(Operand &opnd, ListOperand &srcOpnds, const CCLocInfo &ploc,
11627 bool isSpecialArg, std::vector<RegMapForPhyRegCpy> ®MapForTmpBB)
11628 {
11629 DEBUG_ASSERT(ploc.reg0 != kRinvalid && ploc.reg1 != kRinvalid, "");
11630
11631 auto splitOpnd = SplitInt128(opnd);
11632 auto reg0 = static_cast<AArch64reg>(ploc.reg0);
11633 auto reg1 = static_cast<AArch64reg>(ploc.reg1);
11634 RegOperand &low = GetOrCreatePhysicalRegisterOperand(reg0, k64BitSize, kRegTyInt);
11635 RegOperand &high = GetOrCreatePhysicalRegisterOperand(reg1, k64BitSize, kRegTyInt);
11636
11637 auto cpyTy = PTY_u64;
11638 if (isSpecialArg) {
11639 regMapForTmpBB.emplace_back(RegMapForPhyRegCpy(&low, cpyTy, static_cast<RegOperand *>(&splitOpnd.low), cpyTy));
11640 regMapForTmpBB.emplace_back(
11641 RegMapForPhyRegCpy(&high, cpyTy, static_cast<RegOperand *>(&splitOpnd.high), cpyTy));
11642 } else {
11643 SelectCopy(low, cpyTy, splitOpnd.low, cpyTy);
11644 SelectCopy(high, cpyTy, splitOpnd.high, cpyTy);
11645 }
11646
11647 srcOpnds.PushOpnd(low);
11648 srcOpnds.PushOpnd(high);
11649 }
11650 } /* namespace maplebe */
11651