• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "aarch64_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 &regOpnd = 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