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 ®Opnd = 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