• 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_regsaves.h"
17 #include "aarch64_cg.h"
18 #include "aarch64_live.h"
19 #include "aarch64_cg.h"
20 #include "aarch64_proepilog.h"
21 #include "cg_dominance.h"
22 #include "cg_ssa_pre.h"
23 #include "cg_ssu_pre.h"
24 
25 namespace maplebe {
26 
27 #define RS_DUMP GetEnabledDebug()
28 #define RS_EXTRA (RS_DUMP && true)
29 #define mLog LogInfo::MapleLogger()
30 #define threshold 8
31 #define ONE_REG_AT_A_TIME 0
32 
33 using BBId = uint32;
34 
InitData()35 void AArch64RegSavesOpt::InitData()
36 {
37     calleeBitsDef = cgFunc->GetMemoryPool()->NewArray<CalleeBitsType>(cgFunc->NumBBs());
38     errno_t retDef = memset_s(calleeBitsDef, cgFunc->NumBBs() * sizeof(CalleeBitsType), 0,
39                               cgFunc->NumBBs() * sizeof(CalleeBitsType));
40     calleeBitsUse = cgFunc->GetMemoryPool()->NewArray<CalleeBitsType>(cgFunc->NumBBs());
41     errno_t retUse = memset_s(calleeBitsUse, cgFunc->NumBBs() * sizeof(CalleeBitsType), 0,
42                               cgFunc->NumBBs() * sizeof(CalleeBitsType));
43     CHECK_FATAL(retDef == EOK && retUse == EOK, "memset_s of calleesBits failed");
44 
45     AArch64CGFunc *aarchCGFunc = static_cast<AArch64CGFunc *>(cgFunc);
46     const MapleVector<AArch64reg> &sp = aarchCGFunc->GetCalleeSavedRegs();
47     if (!sp.empty()) {
48         if (std::find(sp.begin(), sp.end(), RFP) != sp.end()) {
49             aarchCGFunc->GetProEpilogSavedRegs().push_back(RFP);
50         }
51         if (std::find(sp.begin(), sp.end(), RLR) != sp.end()) {
52             aarchCGFunc->GetProEpilogSavedRegs().push_back(RLR);
53         }
54     }
55 
56     for (auto bb : bfs->sortedBBs) {
57         SetId2bb(bb);
58     }
59 }
60 
CollectLiveInfo(const BB & bb,const Operand & opnd,bool isDef,bool isUse)61 void AArch64RegSavesOpt::CollectLiveInfo(const BB &bb, const Operand &opnd, bool isDef, bool isUse)
62 {
63     if (!opnd.IsRegister()) {
64         return;
65     }
66     const RegOperand &regOpnd = static_cast<const RegOperand &>(opnd);
67     regno_t regNO = regOpnd.GetRegisterNumber();
68     if (!AArch64Abi::IsCalleeSavedReg(static_cast<AArch64reg>(regNO)) || (regNO >= R29 && regNO <= R31)) {
69         return; /* check only callee-save registers */
70     }
71     RegType regType = regOpnd.GetRegisterType();
72     if (regType == kRegTyVary) {
73         return;
74     }
75     if (isDef) {
76         /* First def */
77         if (!IsCalleeBitSet(GetCalleeBitsDef(), bb.GetId(), regNO)) {
78             SetCalleeBit(GetCalleeBitsDef(), bb.GetId(), regNO);
79         }
80     }
81     if (isUse) {
82         /* Last use */
83         SetCalleeBit(GetCalleeBitsUse(), bb.GetId(), regNO);
84     }
85 }
86 
GenerateReturnBBDefUse(const BB & bb)87 void AArch64RegSavesOpt::GenerateReturnBBDefUse(const BB &bb)
88 {
89     PrimType returnType = cgFunc->GetFunction().GetReturnType()->GetPrimType();
90     AArch64CGFunc *aarchCGFunc = static_cast<AArch64CGFunc *>(cgFunc);
91     if (IsPrimitiveFloat(returnType)) {
92         Operand &phyOpnd =
93             aarchCGFunc->GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(V0), k64BitSize, kRegTyFloat);
94         CollectLiveInfo(bb, phyOpnd, false, true);
95     } else if (IsPrimitiveInteger(returnType)) {
96         Operand &phyOpnd =
97             aarchCGFunc->GetOrCreatePhysicalRegisterOperand(static_cast<AArch64reg>(R0), k64BitSize, kRegTyInt);
98         CollectLiveInfo(bb, phyOpnd, false, true);
99     }
100 }
101 
ProcessAsmListOpnd(const BB & bb,Operand & opnd,uint32 idx)102 void AArch64RegSavesOpt::ProcessAsmListOpnd(const BB &bb, Operand &opnd, uint32 idx)
103 {
104     bool isDef = false;
105     bool isUse = false;
106     switch (idx) {
107         case kAsmOutputListOpnd:
108         case kAsmClobberListOpnd: {
109             isDef = true;
110             break;
111         }
112         case kAsmInputListOpnd: {
113             isUse = true;
114             break;
115         }
116         default:
117             return;
118     }
119     ListOperand &listOpnd = static_cast<ListOperand &>(opnd);
120     for (auto op : listOpnd.GetOperands()) {
121         CollectLiveInfo(bb, *op, isDef, isUse);
122     }
123 }
124 
ProcessListOpnd(const BB & bb,Operand & opnd)125 void AArch64RegSavesOpt::ProcessListOpnd(const BB &bb, Operand &opnd)
126 {
127     ListOperand &listOpnd = static_cast<ListOperand &>(opnd);
128     for (auto op : listOpnd.GetOperands()) {
129         CollectLiveInfo(bb, *op, false, true);
130     }
131 }
132 
ProcessMemOpnd(const BB & bb,Operand & opnd)133 void AArch64RegSavesOpt::ProcessMemOpnd(const BB &bb, Operand &opnd)
134 {
135     auto &memOpnd = static_cast<MemOperand &>(opnd);
136     Operand *base = memOpnd.GetBaseRegister();
137     Operand *offset = memOpnd.GetIndexRegister();
138     if (base != nullptr) {
139         CollectLiveInfo(bb, *base, !memOpnd.IsIntactIndexed(), true);
140     }
141     if (offset != nullptr) {
142         CollectLiveInfo(bb, *offset, false, true);
143     }
144 }
145 
ProcessCondOpnd(const BB & bb)146 void AArch64RegSavesOpt::ProcessCondOpnd(const BB &bb)
147 {
148     Operand &rflag = cgFunc->GetOrCreateRflag();
149     CollectLiveInfo(bb, rflag, false, true);
150 }
151 
152 /* Record in each local BB the 1st def and the last use of a callee-saved
153    register  */
GetLocalDefUse()154 void AArch64RegSavesOpt::GetLocalDefUse()
155 {
156     for (auto bbp : bfs->sortedBBs) {
157         BB &bb = *bbp;
158         if (bb.GetKind() == BB::kBBReturn) {
159             GenerateReturnBBDefUse(bb);
160         }
161         if (bb.IsEmpty()) {
162             continue;
163         }
164 
165         FOR_BB_INSNS(insn, &bb) {
166             if (!insn->IsMachineInstruction()) {
167                 continue;
168             }
169 
170             bool isAsm = (insn->GetMachineOpcode() == MOP_asm);
171             const InsnDesc *md = insn->GetDesc();
172             uint32 opndNum = insn->GetOperandSize();
173             for (uint32 i = 0; i < opndNum; ++i) {
174                 Operand &opnd = insn->GetOperand(i);
175                 auto *regProp = md->opndMD[i];
176                 bool isDef = regProp->IsRegDef();
177                 bool isUse = regProp->IsRegUse();
178                 if (opnd.IsList()) {
179                     if (isAsm) {
180                         ProcessAsmListOpnd(bb, opnd, i);
181                     } else {
182                         ProcessListOpnd(bb, opnd);
183                     }
184                 } else if (opnd.IsMemoryAccessOperand()) {
185                     ProcessMemOpnd(bb, opnd);
186                 } else if (opnd.IsConditionCode()) {
187                     ProcessCondOpnd(bb);
188                 } else {
189                     CollectLiveInfo(bb, opnd, isDef, isUse);
190                 }
191             } /* for all operands */
192         }     /* for all insns */
193     }         /* for all sortedBBs */
194 
195     if (RS_DUMP) {
196         for (uint32 i = 0; i < cgFunc->NumBBs(); ++i) {
197             mLog << i << " : " << calleeBitsDef[i] << " " << calleeBitsUse[i] << "\n";
198             ;
199         }
200     }
201 }
202 
PrintBBs() const203 void AArch64RegSavesOpt::PrintBBs() const
204 {
205     mLog << "RegSaves LiveIn/Out of BFS nodes:\n";
206     for (auto *bb : bfs->sortedBBs) {
207         mLog << "< === > ";
208         mLog << bb->GetId();
209         mLog << " pred:[";
210         for (auto *predBB : bb->GetPreds()) {
211             mLog << " " << predBB->GetId();
212         }
213         mLog << "] succs:[";
214         for (auto *succBB : bb->GetSuccs()) {
215             mLog << " " << succBB->GetId();
216         }
217         mLog << "]\n LiveIn of [" << bb->GetId() << "]: ";
218         for (auto liveIn : bb->GetLiveInRegNO()) {
219             mLog << liveIn << " ";
220         }
221         mLog << "\n LiveOut of [" << bb->GetId() << "]: ";
222         for (auto liveOut : bb->GetLiveOutRegNO()) {
223             mLog << liveOut << " ";
224         }
225         mLog << "\n";
226     }
227 }
228 
229 /* 1st def MUST not have preceding save in dominator list. Each dominator
230    block must not have livein or liveout of the register */
CheckCriteria(BB * bb,regno_t reg) const231 int32 AArch64RegSavesOpt::CheckCriteria(BB *bb, regno_t reg) const
232 {
233     /* Already a site to save */
234     SavedRegInfo *sp = bbSavedRegs[bb->GetId()];
235     if (sp != nullptr && sp->ContainSaveReg(reg)) {
236         return 1; // 1 means reg saved here, skip!
237     }
238 
239     /* This preceding block has livein OR liveout of reg */
240     MapleSet<regno_t> &liveIn = bb->GetLiveInRegNO();
241     MapleSet<regno_t> &liveOut = bb->GetLiveOutRegNO();
242     if (liveIn.find(reg) != liveIn.end() || liveOut.find(reg) != liveOut.end()) {
243         return 2; // 2 means has livein/out, skip!
244     }
245 
246     return 0;
247 }
248 
249 /* Return true if reg is already to be saved in its dominator list */
AlreadySavedInDominatorList(const BB * bb,regno_t reg) const250 bool AArch64RegSavesOpt::AlreadySavedInDominatorList(const BB *bb, regno_t reg) const
251 {
252     BB *aBB = GetDomInfo()->GetDom(bb->GetId());
253 
254     if (RS_DUMP) {
255         mLog << "Checking dom list starting " << bb->GetId() << " for saved R" << (reg - 1) << ":\n  ";
256     }
257     while (!aBB->GetPreds().empty()) { /* can't go beyond prolog */
258         if (RS_DUMP) {
259             mLog << aBB->GetId() << " ";
260         }
261         if (int t = CheckCriteria(aBB, reg)) {
262             if (RS_DUMP) {
263                 if (t == 1) {
264                     mLog << " --R" << (reg - 1) << " saved here, skip!\n";
265                 } else {
266                     mLog << " --R" << (reg - 1) << " has livein/out, skip!\n";
267                 }
268             }
269             return true; /* previously saved, inspect next reg */
270         }
271         aBB = GetDomInfo()->GetDom(aBB->GetId());
272     }
273     return false; /* not previously saved, to save at bb */
274 }
275 
276 /* Determine callee-save regs save locations and record them in bbSavedRegs.
277    Save is needed for a 1st def callee-save register at its dominator block
278    outside any loop. */
DetermineCalleeSaveLocationsDoms()279 void AArch64RegSavesOpt::DetermineCalleeSaveLocationsDoms()
280 {
281     if (RS_DUMP) {
282         mLog << "Determining regsave sites using dom list for " << cgFunc->GetName() << ":\n";
283     }
284     for (auto *bb : bfs->sortedBBs) {
285         if (RS_DUMP) {
286             mLog << "BB: " << bb->GetId() << "\n";
287         }
288         CalleeBitsType c = GetBBCalleeBits(GetCalleeBitsDef(), bb->GetId());
289         if (c == 0) {
290             continue;
291         }
292         CalleeBitsType mask = 1;
293         for (uint32 i = 0; i < static_cast<uint32>(sizeof(CalleeBitsType) << k8BitShift); ++i) {
294             if ((c & mask) != 0) {
295                 MapleSet<regno_t> &liveIn = bb->GetLiveInRegNO();
296                 regno_t reg = ReverseRegBitMap(i);
297                 if (oneAtaTime && oneAtaTimeReg != reg) {
298                     mask <<= 1;
299                     continue;
300                 }
301                 if (liveIn.find(reg) == liveIn.end()) { /* not livein */
302                     BB *bbDom = bb;                     /* start from current BB */
303                     bool done = false;
304                     while (loopInfo.GetBBLoopParent(bbDom->GetId()) != nullptr) {
305                         bbDom = GetDomInfo()->GetDom(bbDom->GetId());
306                         if (CheckCriteria(bbDom, reg)) {
307                             done = true;
308                             break;
309                         }
310                         DEBUG_ASSERT(bbDom, "Can't find dominator for save location");
311                     }
312                     if (done) {
313                         mask <<= 1;
314                         continue;
315                     }
316 
317                     /* Check if a dominator of bbDom was already a location to save */
318                     if (AlreadySavedInDominatorList(bbDom, reg)) {
319                         mask <<= 1;
320                         continue; /* no need to save again, next reg */
321                     }
322 
323                     /* Check if the newly found block is a dominator of block(s) in the current
324                        to be saved list. If so, remove these blocks from bbSavedRegs */
325                     uint32 creg = i;
326                     SavedBBInfo *sp = regSavedBBs[creg];
327                     if (sp == nullptr) {
328                         regSavedBBs[creg] = memPool->New<SavedBBInfo>(alloc);
329                     } else {
330                         for (BB *sbb : sp->GetBBList()) {
331                             for (BB *abb = sbb; !abb->GetPreds().empty();) {
332                                 if (abb->GetId() == bbDom->GetId()) {
333                                     /* Found! Don't plan to save in abb */
334                                     sp->RemoveBB(sbb);
335                                     bbSavedRegs[sbb->GetId()]->RemoveSaveReg(reg);
336                                     if (RS_DUMP) {
337                                         mLog << " --R" << (reg - 1) << " save removed from BB" << sbb->GetId() << "\n";
338                                     }
339                                     break;
340                                 }
341                                 abb = GetDomInfo()->GetDom(abb->GetId());
342                             }
343                         }
344                     }
345                     regSavedBBs[creg]->InsertBB(bbDom);
346 
347                     uint32 bid = bbDom->GetId();
348                     if (RS_DUMP) {
349                         mLog << " --R" << (reg - 1);
350                         mLog << " to save in " << bid << "\n";
351                     }
352                     SavedRegInfo *ctx = GetbbSavedRegsEntry(bid);
353                     if (!ctx->ContainSaveReg(reg)) {
354                         ctx->InsertSaveReg(reg);
355                     }
356                 }
357             }
358             mask <<= 1;
359             CalleeBitsType t = c;
360             t >>= 1;
361             if (t == 0) {
362                 break; /* short cut */
363             }
364         }
365     }
366 }
367 
DetermineCalleeSaveLocationsPre()368 void AArch64RegSavesOpt::DetermineCalleeSaveLocationsPre()
369 {
370     AArch64CGFunc *aarchCGFunc = static_cast<AArch64CGFunc *>(cgFunc);
371     MapleAllocator sprealloc(memPool);
372     if (RS_DUMP) {
373         mLog << "Determining regsave sites using ssa_pre for " << cgFunc->GetName() << ":\n";
374     }
375     const MapleVector<AArch64reg> &callees = aarchCGFunc->GetCalleeSavedRegs();
376     for (auto reg : callees) {
377         if (reg >= R29 && reg < V8) {
378             continue; /* save/restore in prologue, epilogue */
379         }
380         if (oneAtaTime && oneAtaTimeReg != reg) {
381             continue;
382         }
383 
384         SsaPreWorkCand wkCand(&sprealloc);
385         for (uint32 bid = 1; bid < static_cast<uint32>(bbSavedRegs.size()); ++bid) {
386             /* Set the BB occurrences of this callee-saved register */
387             if (IsCalleeBitSet(GetCalleeBitsDef(), bid, reg) || IsCalleeBitSet(GetCalleeBitsUse(), bid, reg)) {
388                 (void)wkCand.occBBs.insert(bid);
389             }
390         }
391         DoSavePlacementOpt(cgFunc, GetDomInfo(), &loopInfo, &wkCand);
392         if (wkCand.saveAtEntryBBs.empty()) {
393             /* something gone wrong, skip this reg */
394             wkCand.saveAtProlog = true;
395         }
396         if (wkCand.saveAtProlog) {
397             /* Save cannot be applied, skip this reg and place save/restore
398                in prolog/epilog */
399             MapleVector<AArch64reg> &pe = aarchCGFunc->GetProEpilogSavedRegs();
400             if (std::find(pe.begin(), pe.end(), reg) == pe.end()) {
401                 pe.push_back(reg);
402             }
403             if (RS_DUMP) {
404                 DEBUG_ASSERT(reg >= 1, "value overflow");
405                 mLog << "Save R" << (reg - 1) << " n/a, do in Pro/Epilog\n";
406             }
407             continue;
408         }
409         if (!wkCand.saveAtEntryBBs.empty()) {
410             for (uint32 entBB : wkCand.saveAtEntryBBs) {
411                 if (RS_DUMP) {
412                     std::string r = reg <= R28 ? "r" : "v";
413                     DEBUG_ASSERT(reg >= 1, "value overflow");
414                     mLog << "BB " << entBB << " save: " << r << (reg - 1) << "\n";
415                 }
416                 GetbbSavedRegsEntry(entBB)->InsertSaveReg(reg);
417             }
418         }
419     }
420 }
421 
422 /* Determine calleesave regs restore locations by calling ssu-pre,
423    previous bbSavedRegs memory is cleared and restore locs recorded in it */
DetermineCalleeRestoreLocations()424 bool AArch64RegSavesOpt::DetermineCalleeRestoreLocations()
425 {
426     AArch64CGFunc *aarchCGFunc = static_cast<AArch64CGFunc *>(cgFunc);
427     MapleAllocator sprealloc(memPool);
428     if (RS_DUMP) {
429         mLog << "Determining Callee Restore Locations:\n";
430     }
431     const MapleVector<AArch64reg> &callees = aarchCGFunc->GetCalleeSavedRegs();
432     for (auto reg : callees) {
433         if (reg >= R29 && reg < V8) {
434             continue; /* save/restore in prologue, epilogue */
435         }
436         if (oneAtaTime && oneAtaTimeReg != reg) {
437             MapleVector<AArch64reg> &pe = aarchCGFunc->GetProEpilogSavedRegs();
438             if (std::find(pe.begin(), pe.end(), reg) == pe.end()) {
439                 pe.push_back(reg);
440             }
441             continue;
442         }
443 
444         SPreWorkCand wkCand(&sprealloc);
445         for (uint32 bid = 1; bid < static_cast<uint32>(bbSavedRegs.size()); ++bid) {
446             /* Set the saved BB locations of this callee-saved register */
447             SavedRegInfo *sp = bbSavedRegs[bid];
448             if (sp != nullptr) {
449                 if (sp->ContainSaveReg(reg)) {
450                     (void)wkCand.saveBBs.insert(bid);
451                 }
452             }
453             /* Set the BB occurrences of this callee-saved register */
454             if (IsCalleeBitSet(GetCalleeBitsDef(), bid, reg) || IsCalleeBitSet(GetCalleeBitsUse(), bid, reg)) {
455                 (void)wkCand.occBBs.insert(bid);
456             }
457         }
458         DoRestorePlacementOpt(cgFunc, GetPostDomInfo(), &wkCand);
459         if (wkCand.saveBBs.empty()) {
460             /* something gone wrong, skip this reg */
461             wkCand.restoreAtEpilog = true;
462         }
463         /* splitted empty block for critical edge present, skip function */
464         MapleSet<uint32> rset = wkCand.restoreAtEntryBBs;
465         for (auto bbid : wkCand.restoreAtExitBBs) {
466             (void)rset.insert(bbid);
467         }
468         for (auto bbid : rset) {
469             BB *bb = GetId2bb(bbid);
470             if (bb->GetKind() == BB::kBBGoto && bb->NumInsn() == 1) {
471                 aarchCGFunc->GetProEpilogSavedRegs().clear();
472                 const MapleVector<AArch64reg> &calleesNew = aarchCGFunc->GetCalleeSavedRegs();
473                 for (auto areg : calleesNew) {
474                     aarchCGFunc->GetProEpilogSavedRegs().push_back(areg);
475                 }
476                 return false;
477             }
478         }
479         if (wkCand.restoreAtEpilog) {
480             /* Restore cannot b3 applied, skip this reg and place save/restore
481                in prolog/epilog */
482             for (size_t bid = 1; bid < bbSavedRegs.size(); ++bid) {
483                 SavedRegInfo *sp = bbSavedRegs[bid];
484                 if (sp != nullptr && !sp->GetSaveSet().empty()) {
485                     if (sp->ContainSaveReg(reg)) {
486                         sp->RemoveSaveReg(reg);
487                     }
488                 }
489             }
490             MapleVector<AArch64reg> &pe = aarchCGFunc->GetProEpilogSavedRegs();
491             if (std::find(pe.begin(), pe.end(), reg) == pe.end()) {
492                 pe.push_back(reg);
493             }
494             if (RS_DUMP) {
495                 DEBUG_ASSERT(reg >= 1, "value overflow");
496                 mLog << "Restore R" << (reg - 1) << " n/a, do in Pro/Epilog\n";
497             }
498             continue;
499         }
500         if (!wkCand.restoreAtEntryBBs.empty() || !wkCand.restoreAtExitBBs.empty()) {
501             for (uint32 entBB : wkCand.restoreAtEntryBBs) {
502                 if (RS_DUMP) {
503                     std::string r = reg <= R28 ? "r" : "v";
504                     DEBUG_ASSERT(reg >= 1, "value overflow");
505                     mLog << "BB " << entBB << " restore: " << r << (reg - 1) << "\n";
506                 }
507                 GetbbSavedRegsEntry(entBB)->InsertEntryReg(reg);
508             }
509             for (uint32 exitBB : wkCand.restoreAtExitBBs) {
510                 BB *bb = GetId2bb(exitBB);
511                 if (bb->GetKind() == BB::kBBIgoto) {
512                     CHECK_FATAL(false, "igoto detected");
513                 }
514                 Insn *lastInsn = bb->GetLastInsn();
515                 if (lastInsn != nullptr && lastInsn->IsBranch() &&
516                     (!lastInsn->GetOperand(0).IsRegister() || /* not a reg OR */
517                         (!AArch64Abi::IsCalleeSavedReg(          /* reg but not cs */
518                             static_cast<AArch64reg>(
519                                 static_cast<RegOperand &>(lastInsn->GetOperand(0)).GetRegisterNumber()))))) {
520                     /* To insert in this block - 1 instr */
521                     SavedRegInfo *sp = GetbbSavedRegsEntry(exitBB);
522                     sp->InsertExitReg(reg);
523                     sp->insertAtLastMinusOne = true;
524                 } else if (bb->GetSuccs().size() > 1) {
525                     for (BB *sbb : bb->GetSuccs()) {
526                         if (sbb->GetPreds().size() > 1) {
527                             CHECK_FATAL(false, "critical edge detected");
528                         }
529                         /* To insert at all succs */
530                         GetbbSavedRegsEntry(sbb->GetId())->InsertEntryReg(reg);
531                     }
532                 } else {
533                     /* otherwise, BB_FT etc */
534                     GetbbSavedRegsEntry(exitBB)->InsertExitReg(reg);
535                 }
536                 if (RS_DUMP) {
537                     std::string r = reg <= R28 ? "R" : "V";
538                     DEBUG_ASSERT(reg >= 1, "value overflow");
539                     mLog << "BB " << exitBB << " restore: " << r << (reg - 1) << "\n";
540                 }
541             }
542         }
543     }
544     return true;
545 }
546 
FindNextOffsetForCalleeSave() const547 int32 AArch64RegSavesOpt::FindNextOffsetForCalleeSave() const
548 {
549     int32 offset = static_cast<int32>(
550         static_cast<AArch64MemLayout *>(cgFunc->GetMemlayout())->RealStackFrameSize() -
551         (static_cast<AArch64CGFunc *>(cgFunc)->SizeOfCalleeSaved() - (kDivide2 * kAarch64IntregBytelen) /* FP/LR */) -
552         cgFunc->GetMemlayout()->SizeOfArgsToStackPass() - cgFunc->GetFunction().GetFrameReseverdSlot());
553 
554     if (cgFunc->GetFunction().GetAttr(FUNCATTR_varargs)) {
555         /* GR/VR save areas are above the callee save area */
556         AArch64MemLayout *ml = static_cast<AArch64MemLayout *>(cgFunc->GetMemlayout());
557         int saveareasize = static_cast<int>(RoundUp(ml->GetSizeOfGRSaveArea(), GetPointerSize() * k2BitSize) +
558                                             RoundUp(ml->GetSizeOfVRSaveArea(), GetPointerSize() * k2BitSize));
559         offset -= saveareasize;
560     }
561     return offset;
562 }
563 
InsertCalleeSaveCode()564 void AArch64RegSavesOpt::InsertCalleeSaveCode()
565 {
566     uint32 bid = 0;
567     BB *saveBB = cgFunc->GetCurBB();
568     AArch64CGFunc *aarchCGFunc = static_cast<AArch64CGFunc *>(cgFunc);
569 
570     if (RS_DUMP) {
571         mLog << "Inserting Save: \n";
572     }
573     int32 offset = FindNextOffsetForCalleeSave();
574     offset +=
575         static_cast<int32>((aarchCGFunc->GetProEpilogSavedRegs().size() - 2) << 3);  // 2 for R29,RLR 3 for 8 bytes
576     for (BB *bb : bfs->sortedBBs) {
577         bid = bb->GetId();
578         aarchCGFunc->SetSplitBaseOffset(0);
579         if (bbSavedRegs[bid] != nullptr && !bbSavedRegs[bid]->GetSaveSet().empty()) {
580             aarchCGFunc->GetDummyBB()->ClearInsns();
581             cgFunc->SetCurBB(*aarchCGFunc->GetDummyBB());
582             AArch64reg intRegFirstHalf = kRinvalid;
583             AArch64reg fpRegFirstHalf = kRinvalid;
584             for (auto areg : bbSavedRegs[bid]->GetSaveSet()) {
585                 AArch64reg reg = static_cast<AArch64reg>(areg);
586                 RegType regType = AArch64isa::IsGPRegister(reg) ? kRegTyInt : kRegTyFloat;
587                 AArch64reg &firstHalf = AArch64isa::IsGPRegister(reg) ? intRegFirstHalf : fpRegFirstHalf;
588                 std::string r = reg <= R28 ? "R" : "V";
589                 /* If reg not seen before, record offset and then update */
590                 if (regOffset.find(areg) == regOffset.end()) {
591                     regOffset[areg] = static_cast<uint32>(offset);
592                     offset += static_cast<int32>(kAarch64IntregBytelen);
593                 }
594                 if (firstHalf == kRinvalid) {
595                     /* 1st half in reg pair */
596                     firstHalf = reg;
597                     if (RS_DUMP && reg > 0) {
598                         mLog << r << (reg - 1) << " save in BB" << bid << "  Offset = " << regOffset[reg] << "\n";
599                     }
600                 } else {
601                     if (regOffset[reg] == (regOffset[firstHalf] + k8ByteSize)) {
602                         /* firstHalf & reg consecutive, make regpair */
603                         AArch64GenProEpilog::AppendInstructionPushPair(*cgFunc, firstHalf, reg, regType,
604                                                                        static_cast<int32>(regOffset[firstHalf]));
605                     } else if (regOffset[firstHalf] == (regOffset[reg] + k8ByteSize)) {
606                         /* reg & firstHalf consecutive, make regpair */
607                         AArch64GenProEpilog::AppendInstructionPushPair(*cgFunc, reg, firstHalf, regType,
608                                                                        static_cast<int32>(regOffset[reg]));
609                     } else {
610                         /* regs cannot be paired */
611                         AArch64GenProEpilog::AppendInstructionPushSingle(*cgFunc, firstHalf, regType,
612                                                                          static_cast<int32>(regOffset[firstHalf]));
613                         AArch64GenProEpilog::AppendInstructionPushSingle(*cgFunc, reg, regType,
614                                                                          static_cast<int32>(regOffset[reg]));
615                     }
616                     firstHalf = kRinvalid;
617                     if (RS_DUMP) {
618                         DEBUG_ASSERT(reg >= 1, "value overflow");
619                         mLog << r << (reg - 1) << " save in BB" << bid << "  Offset = " << regOffset[reg] << "\n";
620                     }
621                 }
622             }
623 
624             if (intRegFirstHalf != kRinvalid) {
625                 AArch64GenProEpilog::AppendInstructionPushSingle(*cgFunc, intRegFirstHalf, kRegTyInt,
626                                                                  static_cast<int32>(regOffset[intRegFirstHalf]));
627             }
628 
629             if (fpRegFirstHalf != kRinvalid) {
630                 AArch64GenProEpilog::AppendInstructionPushSingle(*cgFunc, fpRegFirstHalf, kRegTyFloat,
631                                                                  static_cast<int32>(regOffset[fpRegFirstHalf]));
632             }
633             bb->InsertAtBeginning(*aarchCGFunc->GetDummyBB());
634         }
635     }
636     cgFunc->SetCurBB(*saveBB);
637 }
638 
639 /* DFS to verify the save/restore are in pair(s) within a path */
Verify(regno_t reg,BB * bb,std::set<BB *,BBIdCmp> * visited,BBId * s,BBId * r)640 void AArch64RegSavesOpt::Verify(regno_t reg, BB *bb, std::set<BB *, BBIdCmp> *visited, BBId *s, BBId *r)
641 {
642     (void)visited->insert(bb);
643     BBId bid = bb->GetId();
644     if (RS_EXTRA) {
645         mLog << bid << ","; /* path trace can be long */
646     }
647 
648     if (bbSavedRegs[bid]) {
649         bool entryRestoreMet = false;
650         if (bbSavedRegs[bid]->ContainEntryReg(reg)) {
651             if (RS_EXTRA) {
652                 mLog << "[^" << bid << "],";  // entry restore found
653             }
654             if (*s == 0) {
655                 mLog << "Alert: nR@" << bid << " found w/o save\n";
656                 return;
657             }
658             /* complete s/xR found, continue */
659             mLog << "(" << *s << "," << bid << ") ";
660             *r = bid;
661             entryRestoreMet = true;
662         }
663         if (bbSavedRegs[bid]->ContainSaveReg(reg)) {
664             if (RS_EXTRA) {
665                 mLog << "[" << bid << "],";  // save found
666             }
667             if (*s != 0 && !entryRestoreMet) {
668                 /* another save found before last save restored */
669                 mLog << "Alert: save@" << bid << " found after save@" << *s << "\n";
670                 return;
671             }
672             if (entryRestoreMet) {
673                 *r = 0;
674             }
675             *s = bid;
676         }
677         if (bbSavedRegs[bid]->ContainExitReg(reg)) {
678             if (RS_EXTRA) {
679                 mLog << "[" << bid << "$],";  // exit restore found
680             }
681             if (*s == 0) {
682                 mLog << "Alert: xR@" << bid << " found w/o save\n";
683                 return;
684             }
685             /* complete s/xR found, continue */
686             mLog << "(" << *s << "," << bid << ") ";
687             *r = bid;
688         }
689     }
690 
691     if (bb->GetSuccs().size() == 0) {
692         if (*s != 0 && *r == 0) {
693             mLog << "Alert: save@" << *s << " w/o restore reaches end";
694         }
695         mLog << " <Path " << *s << "->" << bid << " ended>\n";
696         *r = 0;
697     }
698     for (BB *sBB : bb->GetSuccs()) {
699         if (visited->count(sBB) == 0) {
700             Verify(reg, sBB, visited, s, r);
701         }
702     }
703     if (*s == bid) {
704         /* clear only when returned from previous calls to the orig save site */
705         /* clear savebid since all of its succs already visited */
706         *s = 0;
707     }
708     if (*r == bid) {
709         /* clear restorebid if all of its preds already visited */
710         bool clear = true;
711         for (BB *pBB : bb->GetPreds()) {
712             if (visited->count(pBB) == 0) {
713                 clear = false;
714                 break;
715             }
716         }
717         if (clear) {
718             *r = 0;
719         }
720     }
721 }
722 
InsertCalleeRestoreCode()723 void AArch64RegSavesOpt::InsertCalleeRestoreCode()
724 {
725     uint32 bid = 0;
726     BB *saveBB = cgFunc->GetCurBB();
727     AArch64CGFunc *aarchCGFunc = static_cast<AArch64CGFunc *>(cgFunc);
728 
729     if (RS_DUMP) {
730         mLog << "Inserting Restore: \n";
731     }
732     int32 offset = FindNextOffsetForCalleeSave();
733     for (BB *bb : bfs->sortedBBs) {
734         bid = bb->GetId();
735         aarchCGFunc->SetSplitBaseOffset(0);
736         SavedRegInfo *sp = bbSavedRegs[bid];
737         if (sp != nullptr) {
738             if (sp->GetEntrySet().empty() && sp->GetExitSet().empty()) {
739                 continue;
740             }
741 
742             aarchCGFunc->GetDummyBB()->ClearInsns();
743             cgFunc->SetCurBB(*aarchCGFunc->GetDummyBB());
744             for (auto areg : sp->GetEntrySet()) {
745                 AArch64reg reg = static_cast<AArch64reg>(areg);
746                 offset = static_cast<int32>(regOffset[areg]);
747                 if (RS_DUMP) {
748                     std::string r = reg <= R28 ? "R" : "V";
749                     DEBUG_ASSERT(reg >= 1, "value overflow");
750                     mLog << r << (reg - 1) << " entry restore in BB " << bid << "  Saved Offset = " << offset << "\n";
751                     if (RS_EXTRA) {
752                         mLog << "  for save @BB [ ";
753                         for (size_t b = 1; b < bbSavedRegs.size(); ++b) {
754                             if (bbSavedRegs[b] != nullptr && bbSavedRegs[b]->ContainSaveReg(reg)) {
755                                 mLog << b << " ";
756                             }
757                         }
758                         mLog << "]\n";
759                     }
760                 }
761 
762                 /* restore is always the same from saved offset */
763                 RegType regType = AArch64isa::IsGPRegister(reg) ? kRegTyInt : kRegTyFloat;
764                 AArch64GenProEpilog::AppendInstructionPopSingle(*cgFunc, reg, regType, offset);
765             }
766             FOR_BB_INSNS(insn, aarchCGFunc->GetDummyBB()) {
767                 insn->SetDoNotRemove(true); /* do not let ebo remove these restores */
768             }
769             bb->InsertAtBeginning(*aarchCGFunc->GetDummyBB());
770 
771             aarchCGFunc->GetDummyBB()->ClearInsns();
772             cgFunc->SetCurBB(*aarchCGFunc->GetDummyBB());
773             for (auto areg : sp->GetExitSet()) {
774                 AArch64reg reg = static_cast<AArch64reg>(areg);
775                 offset = static_cast<int32>(regOffset[areg]);
776                 if (RS_DUMP) {
777                     std::string r = reg <= R28 ? "R" : "V";
778                     mLog << r << (reg - 1) << " exit restore in BB " << bid << " Offset = " << offset << "\n";
779                     mLog << "  for save @BB [ ";
780                     for (size_t b = 1; b < bbSavedRegs.size(); ++b) {
781                         if (bbSavedRegs[b] != nullptr && bbSavedRegs[b]->ContainSaveReg(reg)) {
782                             mLog << b << " ";
783                         }
784                     }
785                     mLog << "]\n";
786                 }
787 
788                 /* restore is always single from saved offset */
789                 RegType regType = AArch64isa::IsGPRegister(reg) ? kRegTyInt : kRegTyFloat;
790                 AArch64GenProEpilog::AppendInstructionPopSingle(*cgFunc, reg, regType, offset);
791             }
792             FOR_BB_INSNS(insn, aarchCGFunc->GetDummyBB()) {
793                 insn->SetDoNotRemove(true);
794             }
795             if (sp->insertAtLastMinusOne) {
796                 bb->InsertAtEndMinus1(*aarchCGFunc->GetDummyBB());
797             } else {
798                 bb->InsertAtEnd(*aarchCGFunc->GetDummyBB());
799             }
800         }
801     }
802     cgFunc->SetCurBB(*saveBB);
803 }
804 
805 /* Callee-save registers save/restore placement optimization */
Run()806 void AArch64RegSavesOpt::Run()
807 {
808     if (Globals::GetInstance()->GetOptimLevel() <= CGOptions::kLevel1) {
809         return;
810     }
811 
812 #if ONE_REG_AT_A_TIME
813     /* only do reg placement on the following register, others in pro/epilog */
814     oneAtaTime = true;
815     oneAtaTimeReg = R25;
816 #endif
817 
818     Bfs localBfs(*cgFunc, *memPool);
819     bfs = &localBfs;
820     bfs->ComputeBlockOrder();
821     if (RS_DUMP) {
822         mLog << "##Calleeregs Placement for: " << cgFunc->GetName() << "\n";
823         PrintBBs();
824     }
825 
826 #ifdef REDUCE_COMPLEXITY
827     CGOptions::EnableRegSavesOpt();
828     for (auto bb : bfs->sortedBBs) {
829         if (bb->GetSuccs().size() > threshold) {
830             CGOptions::DisableRegSavesOpt();
831             return;
832         }
833     }
834 #endif
835 
836     /* Determined 1st def and last use of all callee-saved registers used
837        for all BBs */
838     InitData();
839     GetLocalDefUse();
840 
841     /* Determine save sites at dominators of 1st def with no live-in and
842        not within loop */
843     if (CGOptions::UseSsaPreSave()) {
844         DetermineCalleeSaveLocationsPre();
845     } else {
846         DetermineCalleeSaveLocationsDoms();
847     }
848 
849     /* Determine restore sites */
850     if (!DetermineCalleeRestoreLocations()) {
851         return;
852     }
853 
854 #ifdef VERIFY
855     /* Verify saves/restores are in pair */
856     if (RS_DUMP) {
857         std::vector<regno_t> rlist = {R19, R20, R21, R22, R23, R24, R25, R26, R27, R28};
858         for (auto reg : rlist) {
859             mLog << "Verify calleeregs_placement data for R" << (reg - 1) << ":\n";
860             std::set<BB *, BBIdCmp> visited;
861             uint32 saveBid = 0;
862             uint32 restoreBid = 0;
863             Verify(reg, cgFunc->GetFirstBB(), &visited, &saveBid, &restoreBid);
864             mLog << "\nVerify Done\n";
865         }
866     }
867 #endif
868 
869     /* Generate callee save instrs at found sites */
870     InsertCalleeSaveCode();
871 
872     /* Generate callee restores at found sites */
873     InsertCalleeRestoreCode();
874 }
875 } /* namespace maplebe */
876