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_strldr.h"
17 #include "aarch64_reaching.h"
18 #include "aarch64_cgfunc.h"
19 #include "common_utils.h"
20
21 namespace maplebe {
22 using namespace maple;
23
SelectMovMop(bool isFloatOrSIMD,bool is64Bit)24 static MOperator SelectMovMop(bool isFloatOrSIMD, bool is64Bit)
25 {
26 return isFloatOrSIMD ? (is64Bit ? MOP_xvmovd : MOP_xvmovs) : (is64Bit ? MOP_xmovrr : MOP_wmovrr);
27 }
28
Run()29 void AArch64StoreLoadOpt::Run()
30 {
31 DoStoreLoadOpt();
32 }
33
GenerateMoveLiveInsn(RegOperand & resRegOpnd,RegOperand & srcRegOpnd,Insn & ldrInsn,Insn & strInsn,short memSeq)34 void AArch64StoreLoadOpt::GenerateMoveLiveInsn(RegOperand &resRegOpnd, RegOperand &srcRegOpnd, Insn &ldrInsn,
35 Insn &strInsn, short memSeq)
36 {
37 MOperator movMop = SelectMovMop(resRegOpnd.IsOfFloatOrSIMDClass(), resRegOpnd.GetSize() == k64BitSize);
38 Insn *movInsn = nullptr;
39 if (str2MovMap[&strInsn][memSeq] != nullptr && !cgFunc.IsAfterRegAlloc()) {
40 Insn *movInsnOfStr = str2MovMap[&strInsn][memSeq];
41 auto &vregOpnd = static_cast<RegOperand &>(movInsnOfStr->GetOperand(kInsnFirstOpnd));
42 movInsn = &cgFunc.GetInsnBuilder()->BuildInsn(movMop, resRegOpnd, vregOpnd);
43 } else {
44 movInsn = &cgFunc.GetInsnBuilder()->BuildInsn(movMop, resRegOpnd, srcRegOpnd);
45 }
46 if (&resRegOpnd == &srcRegOpnd && cgFunc.IsAfterRegAlloc()) {
47 ldrInsn.GetBB()->RemoveInsn(ldrInsn);
48 cgFunc.GetRD()->InitGenUse(*ldrInsn.GetBB(), false);
49 return;
50 }
51 movInsn->SetId(ldrInsn.GetId());
52 ldrInsn.GetBB()->ReplaceInsn(ldrInsn, *movInsn);
53 if (CG_DEBUG_FUNC(cgFunc)) {
54 LogInfo::MapleLogger() << "replace ldrInsn:\n";
55 ldrInsn.Dump();
56 LogInfo::MapleLogger() << "with movInsn:\n";
57 movInsn->Dump();
58 }
59 /* Add comment. */
60 MapleString newComment = ldrInsn.GetComment();
61 if (strInsn.IsStorePair()) {
62 newComment += "; stp-load live version.";
63 } else {
64 newComment += "; str-load live version.";
65 }
66 movInsn->SetComment(newComment);
67 cgFunc.GetRD()->InitGenUse(*ldrInsn.GetBB(), false);
68 }
69
GenerateMoveDeadInsn(RegOperand & resRegOpnd,RegOperand & srcRegOpnd,Insn & ldrInsn,Insn & strInsn,short memSeq)70 void AArch64StoreLoadOpt::GenerateMoveDeadInsn(RegOperand &resRegOpnd, RegOperand &srcRegOpnd, Insn &ldrInsn,
71 Insn &strInsn, short memSeq)
72 {
73 Insn *newMovInsn = nullptr;
74 RegOperand *vregOpnd = nullptr;
75
76 if (str2MovMap[&strInsn][memSeq] == nullptr) {
77 RegType regTy = srcRegOpnd.IsOfFloatOrSIMDClass() ? kRegTyFloat : kRegTyInt;
78 regno_t vRegNO = cgFunc.NewVReg(regTy, srcRegOpnd.GetSize() <= k32BitSize ? k4ByteSize : k8ByteSize);
79 /* generate a new vreg, check if the size of DataInfo is big enough */
80 if (vRegNO >= cgFunc.GetRD()->GetRegSize(*strInsn.GetBB())) {
81 cgFunc.GetRD()->EnlargeRegCapacity(vRegNO);
82 }
83 vregOpnd = &cgFunc.CreateVirtualRegisterOperand(vRegNO);
84 MOperator newMop = SelectMovMop(resRegOpnd.IsOfFloatOrSIMDClass(), resRegOpnd.GetSize() == k64BitSize);
85 newMovInsn = &cgFunc.GetInsnBuilder()->BuildInsn(newMop, *vregOpnd, srcRegOpnd);
86 newMovInsn->SetId(strInsn.GetId() + memSeq + 1);
87 strInsn.GetBB()->InsertInsnAfter(strInsn, *newMovInsn);
88 str2MovMap[&strInsn][memSeq] = newMovInsn;
89 /* update DataInfo */
90 cgFunc.GetRD()->UpdateInOut(*strInsn.GetBB(), true);
91 } else {
92 newMovInsn = str2MovMap[&strInsn][memSeq];
93 vregOpnd = &static_cast<RegOperand &>(newMovInsn->GetOperand(kInsnFirstOpnd));
94 }
95 MOperator movMop = SelectMovMop(resRegOpnd.IsOfFloatOrSIMDClass(), resRegOpnd.GetSize() == k64BitSize);
96 Insn &movInsn = cgFunc.GetInsnBuilder()->BuildInsn(movMop, resRegOpnd, *vregOpnd);
97 movInsn.SetId(ldrInsn.GetId());
98 ldrInsn.GetBB()->ReplaceInsn(ldrInsn, movInsn);
99 if (CG_DEBUG_FUNC(cgFunc)) {
100 LogInfo::MapleLogger() << "replace ldrInsn:\n";
101 ldrInsn.Dump();
102 LogInfo::MapleLogger() << "with movInsn:\n";
103 movInsn.Dump();
104 }
105
106 /* Add comment. */
107 MapleString newComment = ldrInsn.GetComment();
108 if (strInsn.IsStorePair()) {
109 newComment += "; stp-load die version.";
110 } else {
111 newComment += "; str-load die version.";
112 }
113 movInsn.SetComment(newComment);
114 cgFunc.GetRD()->InitGenUse(*ldrInsn.GetBB(), false);
115 }
116
HasMemBarrier(const Insn & ldrInsn,const Insn & strInsn) const117 bool AArch64StoreLoadOpt::HasMemBarrier(const Insn &ldrInsn, const Insn &strInsn) const
118 {
119 if (!cgFunc.GetMirModule().IsCModule()) {
120 return false;
121 }
122 const Insn *currInsn = strInsn.GetNext();
123 while (currInsn != &ldrInsn) {
124 if (currInsn == nullptr) {
125 return false;
126 }
127 if (currInsn->IsMachineInstruction() && currInsn->IsCall()) {
128 return true;
129 }
130 currInsn = currInsn->GetNext();
131 }
132 return false;
133 }
134
135 /*
136 * Transfer: store wzr, [MEM]
137 * ... // May exist branches.
138 * load x200, [MEM]
139 * ==>
140 * OPT_VERSION_STP_ZERO / OPT_VERSION_STR_ZERO:
141 * store wzr, [MEM]
142 * ... // May exist branches. if x100 not dead here.
143 * mov x200, wzr
144 *
145 * Params:
146 * stInsn: indicate store insn.
147 * strSrcIdx: index of source register operand of store insn. (wzr in this example)
148 * memUseInsnSet: insns using memOperand
149 */
DoLoadZeroToMoveTransfer(const Insn & strInsn,short strSrcIdx,const InsnSet & memUseInsnSet) const150 void AArch64StoreLoadOpt::DoLoadZeroToMoveTransfer(const Insn &strInsn, short strSrcIdx,
151 const InsnSet &memUseInsnSet) const
152 {
153 /* comment for strInsn should be only added once */
154 for (auto *ldrInsn : memUseInsnSet) {
155 /* Currently we don't support useInsn is ldp insn. */
156 if (!ldrInsn->IsLoad() || ldrInsn->GetDefRegs().size() > 1) {
157 continue;
158 }
159 if (HasMemBarrier(*ldrInsn, strInsn)) {
160 continue;
161 }
162 /* ldr reg, [mem], the index of [mem] is 1 */
163 InsnSet defInsnForUseInsns = cgFunc.GetRD()->FindDefForMemOpnd(*ldrInsn, 1);
164 /* If load has multiple definition, continue. */
165 if (defInsnForUseInsns.size() > 1) {
166 continue;
167 }
168
169 auto &resOpnd = ldrInsn->GetOperand(0);
170 auto &srcOpnd = strInsn.GetOperand(static_cast<uint32>(strSrcIdx));
171
172 if (resOpnd.GetSize() != srcOpnd.GetSize()) {
173 return;
174 }
175 RegOperand &resRegOpnd = static_cast<RegOperand &>(resOpnd);
176 MOperator movMop = SelectMovMop(resRegOpnd.IsOfFloatOrSIMDClass(), resRegOpnd.GetSize() == k64BitSize);
177 Insn &movInsn = cgFunc.GetInsnBuilder()->BuildInsn(movMop, resOpnd, srcOpnd);
178 movInsn.SetId(ldrInsn->GetId());
179 ldrInsn->GetBB()->ReplaceInsn(*ldrInsn, movInsn);
180
181 /* Add comment. */
182 MapleString newComment = ldrInsn->GetComment();
183 newComment += ", str-load zero version";
184 movInsn.SetComment(newComment);
185 }
186 }
187
CheckStoreOpCode(MOperator opCode) const188 bool AArch64StoreLoadOpt::CheckStoreOpCode(MOperator opCode) const
189 {
190 switch (opCode) {
191 case MOP_wstr:
192 case MOP_xstr:
193 case MOP_sstr:
194 case MOP_dstr:
195 case MOP_wstp:
196 case MOP_xstp:
197 case MOP_sstp:
198 case MOP_dstp:
199 case MOP_wstrb:
200 case MOP_wstrh:
201 return true;
202 default:
203 return false;
204 }
205 }
206
MemPropInit()207 void AArch64StoreLoadOpt::MemPropInit()
208 {
209 propMode = kUndef;
210 amount = 0;
211 removeDefInsn = false;
212 }
213
CheckReplaceReg(Insn & defInsn,Insn & currInsn,InsnSet & replaceRegDefSet,regno_t replaceRegNo)214 bool AArch64StoreLoadOpt::CheckReplaceReg(Insn &defInsn, Insn &currInsn, InsnSet &replaceRegDefSet,
215 regno_t replaceRegNo)
216 {
217 if (replaceRegDefSet.empty()) {
218 return true;
219 }
220 if (defInsn.GetBB() == currInsn.GetBB()) {
221 /* check replace reg def between defInsn and currInsn */
222 Insn *tmpInsn = defInsn.GetNext();
223 while (tmpInsn != nullptr && tmpInsn != &currInsn) {
224 if (replaceRegDefSet.find(tmpInsn) != replaceRegDefSet.end()) {
225 return false;
226 }
227 tmpInsn = tmpInsn->GetNext();
228 }
229 } else {
230 regno_t defRegno = static_cast<RegOperand &>(defInsn.GetOperand(kInsnFirstOpnd)).GetRegisterNumber();
231 if (defRegno == replaceRegNo) {
232 auto *defLoop = loopInfo.GetBBLoopParent(defInsn.GetBB()->GetId());
233 auto defLoopId = defLoop ? defLoop->GetHeader().GetId() : 0;
234 auto *curLoop = loopInfo.GetBBLoopParent(currInsn.GetBB()->GetId());
235 auto curLoopId = curLoop ? curLoop->GetHeader().GetId() : 0;
236 if (defLoopId != curLoopId) {
237 return false;
238 }
239 }
240 AArch64ReachingDefinition *a64RD = static_cast<AArch64ReachingDefinition *>(cgFunc.GetRD());
241 if (a64RD->HasRegDefBetweenInsnGlobal(replaceRegNo, defInsn, currInsn)) {
242 return false;
243 }
244 }
245
246 if (replaceRegDefSet.size() == 1 && *replaceRegDefSet.begin() == &defInsn) {
247 /* lsl x1, x1, #3 <-----should be removed after replace MemOperand of ldrInsn.
248 * ldr x0, [x0,x1] <-----should be single useInsn for x1
249 */
250 InsnSet newRegUseSet = cgFunc.GetRD()->FindUseForRegOpnd(defInsn, replaceRegNo, true);
251 if (newRegUseSet.size() != k1BitSize) {
252 return false;
253 }
254 removeDefInsn = true;
255 }
256 return true;
257 }
258
CheckDefInsn(Insn & defInsn,Insn & currInsn)259 bool AArch64StoreLoadOpt::CheckDefInsn(Insn &defInsn, Insn &currInsn)
260 {
261 if (defInsn.GetOperandSize() < k2ByteSize) {
262 return false;
263 }
264 for (uint32 i = kInsnSecondOpnd; i < defInsn.GetOperandSize(); i++) {
265 Operand &opnd = defInsn.GetOperand(i);
266 if (defInsn.IsMove() && opnd.IsRegister() && !cgFunc.IsSPOrFP(static_cast<RegOperand &>(opnd))) {
267 return false;
268 }
269 if (opnd.IsRegister()) {
270 RegOperand &a64OpndTmp = static_cast<RegOperand &>(opnd);
271 regno_t replaceRegNo = a64OpndTmp.GetRegisterNumber();
272 InsnSet newRegDefSet = cgFunc.GetRD()->FindDefForRegOpnd(currInsn, replaceRegNo, true);
273 if (!CheckReplaceReg(defInsn, currInsn, newRegDefSet, replaceRegNo)) {
274 return false;
275 }
276 }
277 }
278 return true;
279 }
280
CheckNewAmount(const Insn & insn,uint32 newAmount)281 bool AArch64StoreLoadOpt::CheckNewAmount(const Insn &insn, uint32 newAmount)
282 {
283 MOperator mOp = insn.GetMachineOpcode();
284 switch (mOp) {
285 case MOP_wstrb:
286 case MOP_wldrsb:
287 case MOP_xldrsb:
288 case MOP_wldrb: {
289 return newAmount == 0;
290 }
291 case MOP_wstrh:
292 case MOP_wldrsh:
293 case MOP_xldrsh:
294 case MOP_wldrh: {
295 return (newAmount == 0) || (newAmount == k1BitSize);
296 }
297 case MOP_wstr:
298 case MOP_sstr:
299 case MOP_wldr:
300 case MOP_sldr:
301 case MOP_xldrsw: {
302 return (newAmount == 0) || (newAmount == k2BitSize);
303 }
304 case MOP_qstr:
305 case MOP_qldr: {
306 return (newAmount == 0) || (newAmount == k4BitSize);
307 }
308 default: {
309 return (newAmount == 0) || (newAmount == k3ByteSize);
310 }
311 }
312 }
313
CheckNewMemOffset(const Insn & insn,MemOperand * newMemOpnd,uint32 opndIdx)314 bool AArch64StoreLoadOpt::CheckNewMemOffset(const Insn &insn, MemOperand *newMemOpnd, uint32 opndIdx)
315 {
316 AArch64CGFunc &a64CgFunc = static_cast<AArch64CGFunc &>(cgFunc);
317 if ((newMemOpnd->GetOffsetImmediate() != nullptr) &&
318 !a64CgFunc.IsOperandImmValid(insn.GetMachineOpcode(), newMemOpnd, opndIdx)) {
319 return false;
320 }
321 auto newAmount = newMemOpnd->ShiftAmount();
322 if (!CheckNewAmount(insn, newAmount)) {
323 return false;
324 }
325 /* is ldp or stp, addrMode must be BOI */
326 if ((opndIdx == kInsnThirdOpnd) && (newMemOpnd->GetAddrMode() != MemOperand::kAddrModeBOi)) {
327 return false;
328 }
329 return true;
330 }
331
SelectReplaceExt(const Insn & defInsn,RegOperand & base,bool isSigned)332 MemOperand *AArch64StoreLoadOpt::SelectReplaceExt(const Insn &defInsn, RegOperand &base, bool isSigned)
333 {
334 MemOperand *newMemOpnd = nullptr;
335 RegOperand *newOffset = static_cast<RegOperand *>(&defInsn.GetOperand(kInsnSecondOpnd));
336 CHECK_FATAL(newOffset != nullptr, "newOffset is null!");
337 /* defInsn is extend, currMemOpnd is same extend or shift */
338 bool propExtend = (propMode == kPropShift) || ((propMode == kPropSignedExtend) && isSigned) ||
339 ((propMode == kPropUnsignedExtend) && !isSigned);
340 if (propMode == kPropOffset) {
341 newMemOpnd = static_cast<AArch64CGFunc &>(cgFunc).CreateMemOperand(MemOperand::kAddrModeBOrX, k64BitSize, base,
342 *newOffset, 0, isSigned);
343 } else if (propExtend) {
344 newMemOpnd = static_cast<AArch64CGFunc &>(cgFunc).CreateMemOperand(MemOperand::kAddrModeBOrX, k64BitSize, base,
345 *newOffset, amount, isSigned);
346 } else {
347 return nullptr;
348 }
349 return newMemOpnd;
350 }
351
HandleArithImmDef(RegOperand & replace,Operand * oldOffset,int64 defVal)352 MemOperand *AArch64StoreLoadOpt::HandleArithImmDef(RegOperand &replace, Operand *oldOffset, int64 defVal)
353 {
354 if (propMode != kPropBase) {
355 return nullptr;
356 }
357 OfstOperand *newOfstImm = nullptr;
358 if (oldOffset == nullptr) {
359 newOfstImm = &static_cast<AArch64CGFunc &>(cgFunc).CreateOfstOpnd(static_cast<uint64>(defVal), k32BitSize);
360 } else {
361 auto *ofstOpnd = static_cast<OfstOperand *>(oldOffset);
362 CHECK_FATAL(ofstOpnd != nullptr, "oldOffsetOpnd is null");
363 newOfstImm = &static_cast<AArch64CGFunc &>(cgFunc).CreateOfstOpnd(
364 static_cast<uint64>(defVal + ofstOpnd->GetValue()), k32BitSize);
365 }
366 CHECK_FATAL(newOfstImm != nullptr, "newOffset is null!");
367 return static_cast<AArch64CGFunc &>(cgFunc).CreateMemOperand(MemOperand::kAddrModeBOi, k64BitSize, replace, nullptr,
368 newOfstImm, nullptr);
369 }
370
371 /*
372 * limit to adjacent bb to avoid ra spill.
373 */
IsAdjacentBB(Insn & defInsn,Insn & curInsn) const374 bool AArch64StoreLoadOpt::IsAdjacentBB(Insn &defInsn, Insn &curInsn) const
375 {
376 if (defInsn.GetBB() == curInsn.GetBB()) {
377 return true;
378 }
379 for (auto *bb : defInsn.GetBB()->GetSuccs()) {
380 if (bb == curInsn.GetBB()) {
381 return true;
382 }
383 if (bb->IsSoloGoto()) {
384 BB *tragetBB = CGCFG::GetTargetSuc(*bb);
385 if (tragetBB == curInsn.GetBB()) {
386 return true;
387 }
388 }
389 }
390 return false;
391 }
392
CanDoMemProp(const Insn * insn)393 bool AArch64StoreLoadOpt::CanDoMemProp(const Insn *insn)
394 {
395 if (!cgFunc.GetMirModule().IsCModule()) {
396 return false;
397 }
398 if (!insn->IsMachineInstruction()) {
399 return false;
400 }
401 if (insn->GetMachineOpcode() == MOP_qstr) {
402 return false;
403 }
404
405 if (insn->IsLoad() || insn->IsStore()) {
406 if (insn->IsAtomic()) {
407 return false;
408 }
409 // It is not desired to propagate on 128bit reg with immediate offset
410 // which may cause linker to issue misalignment error
411 if (insn->IsAtomic() || insn->GetOperand(0).GetSize() == k128BitSize) {
412 return false;
413 }
414 MemOperand *currMemOpnd = static_cast<MemOperand *>(insn->GetMemOpnd());
415 return currMemOpnd != nullptr;
416 }
417 return false;
418 }
419
SelectPropMode(const MemOperand & currMemOpnd)420 void AArch64StoreLoadOpt::SelectPropMode(const MemOperand &currMemOpnd)
421 {
422 MemOperand::AArch64AddressingMode currAddrMode = currMemOpnd.GetAddrMode();
423 switch (currAddrMode) {
424 case MemOperand::kAddrModeBOi: {
425 if (!currMemOpnd.IsPreIndexed() && !currMemOpnd.IsPostIndexed()) {
426 propMode = kPropBase;
427 }
428 break;
429 }
430 case MemOperand::kAddrModeBOrX: {
431 propMode = kPropOffset;
432 amount = currMemOpnd.ShiftAmount();
433 if (currMemOpnd.GetExtendAsString() == "LSL") {
434 if (amount != 0) {
435 propMode = kPropShift;
436 }
437 break;
438 } else if (currMemOpnd.SignedExtend()) {
439 propMode = kPropSignedExtend;
440 } else if (currMemOpnd.UnsignedExtend()) {
441 propMode = kPropUnsignedExtend;
442 }
443 break;
444 }
445 default:
446 propMode = kUndef;
447 }
448 }
449
450 /*
451 * Optimize: store x100, [MEM]
452 * ... // May exist branches.
453 * load x200, [MEM]
454 * ==>
455 * OPT_VERSION_STP_LIVE / OPT_VERSION_STR_LIVE:
456 * store x100, [MEM]
457 * ... // May exist branches. if x100 not dead here.
458 * mov x200, x100
459 * OPT_VERSION_STP_DIE / OPT_VERSION_STR_DIE:
460 * store x100, [MEM]
461 * mov x9000(new reg), x100
462 * ... // May exist branches. if x100 dead here.
463 * mov x200, x9000
464 *
465 * Note: x100 may be wzr/xzr registers.
466 */
DoStoreLoadOpt()467 void AArch64StoreLoadOpt::DoStoreLoadOpt()
468 {
469 AArch64CGFunc &a64CgFunc = static_cast<AArch64CGFunc &>(cgFunc);
470 if (a64CgFunc.IsIntrnCallForC()) {
471 return;
472 }
473 FOR_ALL_BB(bb, &a64CgFunc) {
474 FOR_BB_INSNS_SAFE(insn, bb, next) {
475 MOperator mOp = insn->GetMachineOpcode();
476 if (CanDoMemProp(insn)) {
477 MemProp(*insn);
478 }
479 if (a64CgFunc.GetMirModule().IsCModule() && cgFunc.GetRD()->OnlyAnalysisReg()) {
480 continue;
481 }
482 if (!insn->IsMachineInstruction() || !insn->IsStore() || !CheckStoreOpCode(mOp) ||
483 (a64CgFunc.GetMirModule().IsCModule() && !a64CgFunc.IsAfterRegAlloc()) ||
484 (!a64CgFunc.GetMirModule().IsCModule() && a64CgFunc.IsAfterRegAlloc())) {
485 continue;
486 }
487 if (insn->IsStorePair()) {
488 ProcessStrPair(*insn);
489 continue;
490 }
491 ProcessStr(*insn);
492 }
493 }
494 }
495
496 /*
497 * PropBase:
498 * add/sub x1, x2, #immVal1
499 * ...(no def of x2)
500 * ldr/str x0, [x1, #immVal2]
501 * ======>
502 * add/sub x1, x2, #immVal1
503 * ...
504 * ldr/str x0, [x2, #(immVal1 + immVal2)/#(-immVal1 + immVal2)]
505 *
506 * PropOffset:
507 * sxtw x2, w2
508 * lsl x1, x2, #1~3
509 * ...(no def of x2)
510 * ldr/str x0, [x0, x1]
511 * ======>
512 * sxtw x2, w2
513 * lsl x1, x2, #1~3
514 * ...
515 * ldr/str x0, [x0, w2, sxtw 1~3]
516 */
MemProp(Insn & insn)517 void AArch64StoreLoadOpt::MemProp(Insn &insn)
518 {
519 MemPropInit();
520 MemOperand *currMemOpnd = static_cast<MemOperand *>(insn.GetMemOpnd());
521 CHECK_NULL_FATAL(currMemOpnd);
522 SelectPropMode(*currMemOpnd);
523 bool memReplaced = false;
524
525 if (propMode == kUndef) {
526 return;
527 }
528
529 /* if prop success, find more prop chance */
530 if (memReplaced) {
531 MemProp(insn);
532 }
533 }
534
535 /*
536 * Assume stack(FP) will not be varied out of pro/epi log
537 * PreIndex:
538 * add/sub x1, x1 #immVal1
539 * ...(no def/use of x1)
540 * ldr/str x0, [x1]
541 * ======>
542 * ldr/str x0, [x1, #immVal1]!
543 *
544 * PostIndex:
545 * ldr/str x0, [x1]
546 * ...(no def/use of x1)
547 * add/sub x1, x1, #immVal1
548 * ======>
549 * ldr/str x0, [x1], #immVal1
550 */
StrLdrIndexModeOpt(Insn & currInsn)551 void AArch64StoreLoadOpt::StrLdrIndexModeOpt(Insn &currInsn)
552 {
553 auto *curMemopnd = static_cast<MemOperand *>(currInsn.GetMemOpnd());
554 DEBUG_ASSERT(curMemopnd != nullptr, " get memopnd failed");
555 /* one instruction cannot define one register twice */
556 if (!CanDoIndexOpt(*curMemopnd) || currInsn.IsRegDefined(curMemopnd->GetBaseRegister()->GetRegisterNumber())) {
557 return;
558 }
559 MemOperand *newMemopnd = SelectIndexOptMode(currInsn, *curMemopnd);
560 if (newMemopnd != nullptr) {
561 currInsn.SetMemOpnd(newMemopnd);
562 }
563 }
564
CanDoIndexOpt(const MemOperand & MemOpnd)565 bool AArch64StoreLoadOpt::CanDoIndexOpt(const MemOperand &MemOpnd)
566 {
567 if (MemOpnd.GetAddrMode() != MemOperand::kAddrModeBOi || !MemOpnd.IsIntactIndexed()) {
568 return false;
569 }
570 DEBUG_ASSERT(MemOpnd.GetOffsetImmediate() != nullptr, " kAddrModeBOi memopnd have no offset imm");
571 if (!MemOpnd.GetOffsetImmediate()->IsImmOffset()) {
572 return false;
573 }
574 if (cgFunc.IsSPOrFP(*MemOpnd.GetBaseRegister())) {
575 return false;
576 }
577 OfstOperand *a64Ofst = MemOpnd.GetOffsetImmediate();
578 if (a64Ofst == nullptr) {
579 return false;
580 }
581 return a64Ofst->GetValue() == 0;
582 }
583
GetOffsetForNewIndex(Insn & defInsn,Insn & insn,regno_t baseRegNO,uint32 memOpndSize)584 int64 AArch64StoreLoadOpt::GetOffsetForNewIndex(Insn &defInsn, Insn &insn, regno_t baseRegNO, uint32 memOpndSize)
585 {
586 bool subMode = defInsn.GetMachineOpcode() == MOP_wsubrri12 || defInsn.GetMachineOpcode() == MOP_xsubrri12;
587 bool addMode = defInsn.GetMachineOpcode() == MOP_waddrri12 || defInsn.GetMachineOpcode() == MOP_xaddrri12;
588 if (addMode || subMode) {
589 DEBUG_ASSERT(static_cast<RegOperand &>(defInsn.GetOperand(kInsnFirstOpnd)).GetRegisterNumber() == baseRegNO,
590 "check def opnd");
591 auto &srcOpnd = static_cast<RegOperand &>(defInsn.GetOperand(kInsnSecondOpnd));
592 if (srcOpnd.GetRegisterNumber() == baseRegNO && defInsn.GetBB() == insn.GetBB()) {
593 int64 offsetVal = static_cast<ImmOperand &>(defInsn.GetOperand(kInsnThirdOpnd)).GetValue();
594 if (!MemOperand::IsSIMMOffsetOutOfRange(offsetVal, memOpndSize == k64BitSize, insn.IsLoadStorePair())) {
595 return subMode ? -offsetVal : offsetVal;
596 }
597 }
598 }
599 return kMaxPimm8; /* simm max value cannot excced pimm max value */
600 };
601
SelectIndexOptMode(Insn & insn,const MemOperand & curMemOpnd)602 MemOperand *AArch64StoreLoadOpt::SelectIndexOptMode(Insn &insn, const MemOperand &curMemOpnd)
603 {
604 AArch64ReachingDefinition *a64RD = static_cast<AArch64ReachingDefinition *>(cgFunc.GetRD());
605 DEBUG_ASSERT((a64RD != nullptr), "check a64RD!");
606 regno_t baseRegisterNO = curMemOpnd.GetBaseRegister()->GetRegisterNumber();
607 auto &a64cgFunc = static_cast<AArch64CGFunc &>(cgFunc);
608 /* pre index */
609 InsnSet regDefSet = a64RD->FindDefForRegOpnd(insn, baseRegisterNO, true);
610 if (regDefSet.size() == k1BitSize) {
611 Insn *defInsn = *regDefSet.begin();
612 int64 defOffset = GetOffsetForNewIndex(*defInsn, insn, baseRegisterNO, curMemOpnd.GetSize());
613 if (defOffset < kMaxPimm8) {
614 InsnSet tempCheck;
615 (void)a64RD->FindRegUseBetweenInsn(baseRegisterNO, defInsn->GetNext(), insn.GetPrev(), tempCheck);
616 if (tempCheck.empty() && (defInsn->GetBB() == insn.GetBB())) {
617 auto &newMem = a64cgFunc.CreateMemOpnd(*curMemOpnd.GetBaseRegister(), defOffset, curMemOpnd.GetSize());
618 DEBUG_ASSERT(newMem.GetOffsetImmediate() != nullptr, "need offset for memopnd in this case");
619 newMem.SetIndexOpt(MemOperand::kPreIndex);
620 insn.GetBB()->RemoveInsn(*defInsn);
621 return &newMem;
622 }
623 }
624 }
625 /* post index */
626 std::vector<Insn *> refDefVec =
627 a64RD->FindRegDefBetweenInsn(baseRegisterNO, &insn, insn.GetBB()->GetLastInsn(), true);
628 if (!refDefVec.empty()) {
629 Insn *defInsn = refDefVec.back();
630 int64 defOffset = GetOffsetForNewIndex(*defInsn, insn, baseRegisterNO, curMemOpnd.GetSize());
631 if (defOffset < kMaxPimm8) {
632 InsnSet tempCheck;
633 (void)a64RD->FindRegUseBetweenInsn(baseRegisterNO, insn.GetNext(), defInsn->GetPrev(), tempCheck);
634 if (tempCheck.empty() && (defInsn->GetBB() == insn.GetBB())) {
635 auto &newMem = a64cgFunc.CreateMemOpnd(*curMemOpnd.GetBaseRegister(), defOffset, curMemOpnd.GetSize());
636 DEBUG_ASSERT(newMem.GetOffsetImmediate() != nullptr, "need offset for memopnd in this case");
637 newMem.SetIndexOpt(MemOperand::kPostIndex);
638 insn.GetBB()->RemoveInsn(*defInsn);
639 return &newMem;
640 }
641 }
642 }
643 return nullptr;
644 }
645
ProcessStrPair(Insn & insn)646 void AArch64StoreLoadOpt::ProcessStrPair(Insn &insn)
647 {
648 const short memIndex = 2;
649 short regIndex = 0;
650 Operand &opnd = insn.GetOperand(memIndex);
651 auto &memOpnd = static_cast<MemOperand &>(opnd);
652 RegOperand *base = memOpnd.GetBaseRegister();
653 if ((base == nullptr) || !(cgFunc.GetRD()->IsFrameReg(*base))) {
654 return;
655 }
656 if (cgFunc.IsAfterRegAlloc() && !insn.IsSpillInsn()) {
657 return;
658 }
659 DEBUG_ASSERT(memOpnd.GetIndexRegister() == nullptr, "frame MemOperand must not be exist register index");
660 InsnSet memUseInsnSet;
661 for (int i = 0; i != kMaxMovNum; ++i) {
662 memUseInsnSet.clear();
663 if (i == 0) {
664 regIndex = 0;
665 memUseInsnSet = cgFunc.GetRD()->FindUseForMemOpnd(insn, memIndex);
666 } else {
667 regIndex = 1;
668 memUseInsnSet = cgFunc.GetRD()->FindUseForMemOpnd(insn, memIndex, true);
669 }
670 if (memUseInsnSet.empty()) {
671 return;
672 }
673 auto ®Opnd = static_cast<RegOperand &>(insn.GetOperand(static_cast<uint32>(regIndex)));
674 if (regOpnd.GetRegisterNumber() == RZR) {
675 DoLoadZeroToMoveTransfer(insn, regIndex, memUseInsnSet);
676 }
677 }
678 }
679
ProcessStr(Insn & insn)680 void AArch64StoreLoadOpt::ProcessStr(Insn &insn)
681 {
682 /* str x100, [mem], mem index is 1, x100 index is 0; */
683 const short memIndex = 1;
684 const short regIndex = 0;
685 Operand &opnd = insn.GetOperand(memIndex);
686 auto &memOpnd = static_cast<MemOperand &>(opnd);
687 RegOperand *base = memOpnd.GetBaseRegister();
688 if ((base == nullptr) || !(cgFunc.GetRD()->IsFrameReg(*base))) {
689 return;
690 }
691
692 if (cgFunc.IsAfterRegAlloc() && !insn.IsSpillInsn()) {
693 return;
694 }
695 DEBUG_ASSERT(memOpnd.GetIndexRegister() == nullptr, "frame MemOperand must not be exist register index");
696
697 InsnSet memUseInsnSet = cgFunc.GetRD()->FindUseForMemOpnd(insn, memIndex);
698 if (memUseInsnSet.empty()) {
699 return;
700 }
701
702 auto *regOpnd = static_cast<RegOperand *>(&insn.GetOperand(regIndex));
703 CHECK_NULL_FATAL(regOpnd);
704 if (regOpnd->GetRegisterNumber() == RZR) {
705 DoLoadZeroToMoveTransfer(insn, regIndex, memUseInsnSet);
706 }
707 if (cgFunc.IsAfterRegAlloc() && insn.IsSpillInsn()) {
708 InsnSet newmemUseInsnSet = cgFunc.GetRD()->FindUseForMemOpnd(insn, memIndex);
709 if (newmemUseInsnSet.empty()) {
710 insn.GetBB()->RemoveInsn(insn);
711 }
712 }
713 }
714 } /* namespace maplebe */
715