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 "reg_alloc_basic.h"
17 #include "cg.h"
18
19 namespace maplebe {
20 /*
21 * NB. As an optimization we can use X8 as a scratch (temporary)
22 * register if the return value is not returned through memory.
23 */
HandleRegOpnd(Operand & opnd)24 Operand *DefaultO0RegAllocator::HandleRegOpnd(Operand &opnd)
25 {
26 DEBUG_ASSERT(opnd.IsRegister(), "Operand should be register operand");
27 auto ®Opnd = static_cast<RegOperand &>(opnd);
28 if (regOpnd.IsOfCC()) {
29 return &opnd;
30 }
31 if (!regInfo->IsVirtualRegister(regOpnd)) {
32 availRegSet[regOpnd.GetRegisterNumber()] = false;
33 (void)liveReg.insert(regOpnd.GetRegisterNumber());
34 return ®Opnd;
35 }
36 auto regMapIt = regMap.find(regOpnd.GetRegisterNumber());
37 if (regMapIt != regMap.end()) { /* already allocated this register */
38 DEBUG_ASSERT(regMapIt->second < regInfo->GetAllRegNum(), "must be a physical register");
39 regno_t newRegNO = regMapIt->second;
40 availRegSet[newRegNO] = false; /* make sure the real register can not be allocated and live */
41 (void)liveReg.insert(newRegNO);
42 (void)allocatedSet.insert(&opnd);
43 return &cgFunc->GetOpndBuilder()->CreatePReg(newRegNO, regOpnd.GetSize(), regOpnd.GetRegisterType());
44 }
45 if (AllocatePhysicalRegister(regOpnd)) {
46 (void)allocatedSet.insert(&opnd);
47 auto regMapItSecond = regMap.find(regOpnd.GetRegisterNumber());
48 DEBUG_ASSERT(regMapItSecond != regMap.end(), " ERROR: can not find register number in regmap ");
49 return &cgFunc->GetOpndBuilder()->CreatePReg(regMapItSecond->second, regOpnd.GetSize(),
50 regOpnd.GetRegisterType());
51 }
52
53 /* use 0 register as spill register */
54 regno_t regNO = 0;
55 return &cgFunc->GetOpndBuilder()->CreatePReg(regNO, regOpnd.GetSize(), regOpnd.GetRegisterType());
56 }
57
HandleMemOpnd(Operand & opnd)58 Operand *DefaultO0RegAllocator::HandleMemOpnd(Operand &opnd)
59 {
60 DEBUG_ASSERT(opnd.IsMemoryAccessOperand(), "Operand should be memory access operand");
61 auto *memOpnd = static_cast<MemOperand *>(&opnd);
62 Operand *res = nullptr;
63 if (memOpnd->GetBaseRegister() != nullptr) {
64 res = AllocSrcOpnd(*memOpnd->GetBaseRegister());
65 memOpnd->SetBaseRegister(static_cast<RegOperand &>(*res));
66 }
67 if (memOpnd->GetIndexRegister() != nullptr) {
68 res = AllocSrcOpnd(*memOpnd->GetIndexRegister());
69 memOpnd->SetIndexRegister(static_cast<RegOperand &>(*res));
70 }
71 (void)allocatedSet.insert(&opnd);
72 return memOpnd;
73 }
74
AllocSrcOpnd(Operand & opnd)75 Operand *DefaultO0RegAllocator::AllocSrcOpnd(Operand &opnd)
76 {
77 if (opnd.IsRegister()) {
78 if (regInfo->IsUnconcernedReg(static_cast<RegOperand &>(opnd))) {
79 return &opnd;
80 }
81 return HandleRegOpnd(opnd);
82 } else if (opnd.IsMemoryAccessOperand()) {
83 return HandleMemOpnd(opnd);
84 }
85 DEBUG_ASSERT(false, "NYI");
86 return nullptr;
87 }
88
AllocDestOpnd(Operand & opnd,const Insn & insn)89 Operand *DefaultO0RegAllocator::AllocDestOpnd(Operand &opnd, const Insn &insn)
90 {
91 if (!opnd.IsRegister()) {
92 DEBUG_ASSERT(false, "result operand must be of type register");
93 return nullptr;
94 }
95 auto ®Opnd = static_cast<RegOperand &>(opnd);
96 if (regInfo->IsUnconcernedReg(static_cast<RegOperand &>(opnd))) {
97 return &opnd;
98 }
99 if (!regInfo->IsVirtualRegister(regOpnd)) {
100 auto reg = regOpnd.GetRegisterNumber();
101 availRegSet[reg] = true;
102 uint32 id = GetRegLivenessId(regOpnd.GetRegisterNumber());
103 if (id != 0 && id <= insn.GetId()) {
104 ReleaseReg(reg);
105 }
106 return &opnd;
107 }
108
109 auto regMapIt = regMap.find(regOpnd.GetRegisterNumber());
110 if (regMapIt != regMap.end()) {
111 regno_t reg = regMapIt->second;
112 if (!insn.IsCondDef()) {
113 uint32 id = GetRegLivenessId(regOpnd.GetRegisterNumber());
114 if (id != 0 && id <= insn.GetId()) {
115 ReleaseReg(reg);
116 }
117 }
118 } else {
119 /* AllocatePhysicalRegister insert a mapping from vreg no to phy reg no into regMap */
120 if (AllocatePhysicalRegister(regOpnd)) {
121 regMapIt = regMap.find(regOpnd.GetRegisterNumber());
122 if (!insn.IsCondDef()) {
123 uint32 id = GetRegLivenessId(regOpnd.GetRegisterNumber());
124 if (id && (id <= insn.GetId())) {
125 ReleaseReg(regMapIt->second);
126 }
127 }
128 } else {
129 /* For register spill. use 0 register as spill register */
130 regno_t regNO = 0;
131 return &cgFunc->GetOpndBuilder()->CreatePReg(regNO, regOpnd.GetSize(), regOpnd.GetRegisterType());
132 }
133 }
134 (void)allocatedSet.insert(&opnd);
135 return &cgFunc->GetOpndBuilder()->CreatePReg(regMapIt->second, regOpnd.GetSize(), regOpnd.GetRegisterType());
136 }
137
GetPhysicalRegisterBank(RegType regTy,regno_t & begin,regno_t & end) const138 void DefaultO0RegAllocator::GetPhysicalRegisterBank(RegType regTy, regno_t &begin, regno_t &end) const
139 {
140 switch (regTy) {
141 case kRegTyVary:
142 case kRegTyCc:
143 break;
144 case kRegTyInt:
145 begin = *regInfo->GetIntRegs().begin();
146 end = *regInfo->GetIntRegs().rbegin();
147 break;
148 case kRegTyFloat:
149 begin = *regInfo->GetFpRegs().begin();
150 end = *regInfo->GetFpRegs().rbegin();
151 break;
152 default:
153 DEBUG_ASSERT(false, "NYI");
154 break;
155 }
156 }
157
InitAvailReg()158 void DefaultO0RegAllocator::InitAvailReg()
159 {
160 for (auto it : regInfo->GetAllRegs()) {
161 availRegSet[it] = true;
162 }
163 }
164
ReleaseReg(const RegOperand & regOpnd)165 void DefaultO0RegAllocator::ReleaseReg(const RegOperand ®Opnd)
166 {
167 ReleaseReg(regMap[regOpnd.GetRegisterNumber()]);
168 }
169
ReleaseReg(regno_t reg)170 void DefaultO0RegAllocator::ReleaseReg(regno_t reg)
171 {
172 DEBUG_ASSERT(reg < regInfo->GetAllRegNum(), "can't release virtual register");
173 liveReg.erase(reg);
174 /* Special registers can not be allocated */
175 if (!regInfo->IsUnconcernedReg(reg)) {
176 availRegSet[reg] = true;
177 }
178 }
179
180 /* trying to allocate a physical register to opnd. return true if success */
AllocatePhysicalRegister(const RegOperand & opnd)181 bool DefaultO0RegAllocator::AllocatePhysicalRegister(const RegOperand &opnd)
182 {
183 RegType regType = opnd.GetRegisterType();
184 regno_t regNo = opnd.GetRegisterNumber();
185 regno_t regStart = 0;
186 regno_t regEnd = 0;
187 GetPhysicalRegisterBank(regType, regStart, regEnd);
188
189 const auto opndRegIt = regLiveness.find(regNo);
190 for (regno_t reg = regStart; reg <= regEnd; ++reg) {
191 if (!availRegSet[reg]) {
192 continue;
193 }
194
195 if (opndRegIt != regLiveness.end()) {
196 const auto regIt = regLiveness.find(reg);
197 DEBUG_ASSERT(opndRegIt->second.size() == 1, "NIY, opnd reg liveness range must be 1.");
198 if (regIt != regLiveness.end() && CheckRangesOverlap(opndRegIt->second.front(), regIt->second)) {
199 continue;
200 }
201 }
202
203 regMap[opnd.GetRegisterNumber()] = reg;
204 availRegSet[reg] = false;
205 (void)liveReg.insert(reg); /* this register is live now */
206 return true;
207 }
208 return false;
209 }
210
211 /* If opnd is a callee saved register, save it in the prolog and restore it in the epilog */
SaveCalleeSavedReg(const RegOperand & regOpnd)212 void DefaultO0RegAllocator::SaveCalleeSavedReg(const RegOperand ®Opnd)
213 {
214 regno_t regNO = regOpnd.GetRegisterNumber();
215 auto phyReg = regInfo->IsVirtualRegister(regOpnd) ? regMap[regNO] : regNO;
216 /* when yieldpoint is enabled, skip the reserved register for yieldpoint. */
217 if (cgFunc->GetCG()->GenYieldPoint() && (regInfo->IsYieldPointReg(phyReg))) {
218 return;
219 }
220
221 if (regInfo->IsCalleeSavedReg(phyReg)) {
222 calleeSaveUsed.insert(phyReg);
223 }
224 }
225
GetRegLivenessId(regno_t regNo)226 uint32 DefaultO0RegAllocator::GetRegLivenessId(regno_t regNo)
227 {
228 auto regIt = regLiveness.find(regNo);
229 return ((regIt == regLiveness.end()) ? 0 : regIt->second.back().second);
230 }
231
CheckRangesOverlap(const std::pair<uint32,uint32> & range1,const MapleVector<std::pair<uint32,uint32>> & ranges2) const232 bool DefaultO0RegAllocator::CheckRangesOverlap(const std::pair<uint32, uint32> &range1,
233 const MapleVector<std::pair<uint32, uint32>> &ranges2) const
234 {
235 /*
236 * Check whether range1 and ranges2 overlap.
237 * The ranges2 is sorted.
238 */
239 auto pos = std::lower_bound(
240 ranges2.begin(), ranges2.end(), range1,
241 [](const std::pair<uint32, uint32> &r2, const std::pair<uint32, uint32> &r1) { return r1.first >= r2.second; });
242 if (pos == ranges2.end()) {
243 return false;
244 }
245 auto &range2 = *pos;
246 if (std::max(range1.first, range2.first) <= std::min(range1.second, range2.second)) {
247 return true;
248 }
249 return false;
250 }
251
SetupRegLiveness(BB * bb)252 void DefaultO0RegAllocator::SetupRegLiveness(BB *bb)
253 {
254 regLiveness.clear();
255
256 uint32 id = 1;
257 FOR_BB_INSNS_REV(insn, bb) {
258 if (!insn->IsMachineInstruction()) {
259 continue;
260 }
261 insn->SetId(id);
262 id++;
263 uint32 opndNum = insn->GetOperandSize();
264 const InsnDesc *curMd = insn->GetDesc();
265 for (uint32 i = 0; i < opndNum; i++) {
266 Operand &opnd = insn->GetOperand(i);
267 const OpndDesc *opndDesc = curMd->GetOpndDes(i);
268 if (opnd.IsRegister()) {
269 /* def-use is processed by use */
270 SetupRegLiveness(static_cast<RegOperand &>(opnd), insn->GetId(), !opndDesc->IsUse());
271 } else if (opnd.IsMemoryAccessOperand()) {
272 SetupRegLiveness(static_cast<MemOperand &>(opnd), insn->GetId());
273 } else if (opnd.IsList()) {
274 SetupRegLiveness(static_cast<ListOperand &>(opnd), insn->GetId(), opndDesc->IsDef());
275 }
276 }
277 }
278
279 /* clear the last empty range */
280 for (auto ®LivenessIt : regLiveness) {
281 auto ®LivenessRanges = regLivenessIt.second;
282 if (regLivenessRanges.back().first == 0) {
283 regLivenessRanges.pop_back();
284 }
285 }
286 }
287
SetupRegLiveness(MemOperand & opnd,uint32 insnId)288 void DefaultO0RegAllocator::SetupRegLiveness(MemOperand &opnd, uint32 insnId)
289 {
290 /* base regOpnd is use in O0 */
291 if (opnd.GetBaseRegister()) {
292 SetupRegLiveness(*opnd.GetBaseRegister(), insnId, false);
293 }
294 /* index regOpnd must be use */
295 if (opnd.GetIndexRegister()) {
296 SetupRegLiveness(*opnd.GetIndexRegister(), insnId, false);
297 }
298 }
299
SetupRegLiveness(ListOperand & opnd,uint32 insnId,bool isDef)300 void DefaultO0RegAllocator::SetupRegLiveness(ListOperand &opnd, uint32 insnId, bool isDef)
301 {
302 for (RegOperand *regOpnd : opnd.GetOperands()) {
303 SetupRegLiveness(*regOpnd, insnId, isDef);
304 }
305 }
306
SetupRegLiveness(RegOperand & opnd,uint32 insnId,bool isDef)307 void DefaultO0RegAllocator::SetupRegLiveness(RegOperand &opnd, uint32 insnId, bool isDef)
308 {
309 MapleVector<std::pair<uint32, uint32>> ranges(alloc.Adapter());
310 auto regLivenessIt = regLiveness.emplace(opnd.GetRegisterNumber(), ranges).first;
311 auto ®LivenessRanges = regLivenessIt->second;
312 if (regLivenessRanges.empty()) {
313 regLivenessRanges.push_back(std::make_pair(0, 0));
314 }
315 auto ®LivenessLastRange = regLivenessRanges.back();
316 if (regLivenessLastRange.first == 0) {
317 regLivenessLastRange.first = insnId;
318 }
319 regLivenessLastRange.second = insnId;
320
321 /* create new range, only phyReg need to be segmented */
322 if (isDef && regInfo->IsAvailableReg(opnd.GetRegisterNumber())) {
323 regLivenessRanges.push_back(std::make_pair(0, 0));
324 }
325 }
326
AllocHandleDestList(Insn & insn,Operand & opnd,uint32 idx)327 void DefaultO0RegAllocator::AllocHandleDestList(Insn &insn, Operand &opnd, uint32 idx)
328 {
329 if (!opnd.IsList()) {
330 return;
331 }
332 auto *listOpnds = &static_cast<ListOperand &>(opnd);
333 auto *listOpndsNew = &cgFunc->GetOpndBuilder()->CreateList();
334 for (auto *dstOpnd : listOpnds->GetOperands()) {
335 if (allocatedSet.find(dstOpnd) != allocatedSet.end()) {
336 auto ®Opnd = static_cast<RegOperand &>(*dstOpnd);
337 SaveCalleeSavedReg(regOpnd);
338 listOpndsNew->PushOpnd(cgFunc->GetOpndBuilder()->CreatePReg(regMap[regOpnd.GetRegisterNumber()],
339 regOpnd.GetSize(), regOpnd.GetRegisterType()));
340 continue; /* already allocated */
341 }
342 RegOperand *regOpnd = static_cast<RegOperand *>(AllocDestOpnd(*dstOpnd, insn));
343 DEBUG_ASSERT(regOpnd != nullptr, "null ptr check");
344 auto physRegno = regOpnd->GetRegisterNumber();
345 availRegSet[physRegno] = false;
346 (void)liveReg.insert(physRegno);
347 listOpndsNew->PushOpnd(
348 cgFunc->GetOpndBuilder()->CreatePReg(physRegno, regOpnd->GetSize(), regOpnd->GetRegisterType()));
349 }
350 insn.SetOperand(idx, *listOpndsNew);
351 for (auto *dstOpnd : listOpndsNew->GetOperands()) {
352 uint32 id = GetRegLivenessId(dstOpnd->GetRegisterNumber());
353 if (id != 0 && id <= insn.GetId()) {
354 ReleaseReg(*dstOpnd);
355 }
356 }
357 }
358
AllocHandleDest(Insn & insn,Operand & opnd,uint32 idx)359 void DefaultO0RegAllocator::AllocHandleDest(Insn &insn, Operand &opnd, uint32 idx)
360 {
361 if (allocatedSet.find(&opnd) != allocatedSet.end()) {
362 /* free the live range of this register */
363 auto ®Opnd = static_cast<RegOperand &>(opnd);
364 SaveCalleeSavedReg(regOpnd);
365 if (insn.IsAtomicStore() || insn.IsSpecialIntrinsic()) {
366 /* remember the physical machine register assigned */
367 regno_t regNO = regOpnd.GetRegisterNumber();
368 rememberRegs.push_back(regInfo->IsVirtualRegister(regOpnd) ? regMap[regNO] : regNO);
369 } else if (!insn.IsCondDef()) {
370 uint32 id = GetRegLivenessId(regOpnd.GetRegisterNumber());
371 if (id != 0 && id <= insn.GetId()) {
372 ReleaseReg(regOpnd);
373 }
374 }
375 insn.SetOperand(idx, cgFunc->GetOpndBuilder()->CreatePReg(regMap[regOpnd.GetRegisterNumber()],
376 regOpnd.GetSize(), regOpnd.GetRegisterType()));
377 return; /* already allocated */
378 }
379
380 if (opnd.IsRegister()) {
381 insn.SetOperand(idx, *AllocDestOpnd(opnd, insn));
382 SaveCalleeSavedReg(static_cast<RegOperand &>(opnd));
383 }
384 }
385
AllocHandleSrcList(Insn & insn,Operand & opnd,uint32 idx)386 void DefaultO0RegAllocator::AllocHandleSrcList(Insn &insn, Operand &opnd, uint32 idx)
387 {
388 if (!opnd.IsList()) {
389 return;
390 }
391 auto *listOpnds = &static_cast<ListOperand &>(opnd);
392 auto *listOpndsNew = &cgFunc->GetOpndBuilder()->CreateList();
393 for (auto *srcOpnd : listOpnds->GetOperands()) {
394 if (allocatedSet.find(srcOpnd) != allocatedSet.end()) {
395 auto *regOpnd = static_cast<RegOperand *>(srcOpnd);
396 regno_t reg = regMap[regOpnd->GetRegisterNumber()];
397 availRegSet[reg] = false;
398 (void)liveReg.insert(reg); /* this register is live now */
399 listOpndsNew->PushOpnd(
400 cgFunc->GetOpndBuilder()->CreatePReg(reg, regOpnd->GetSize(), regOpnd->GetRegisterType()));
401 continue; /* already allocated */
402 }
403 RegOperand *regOpnd = static_cast<RegOperand *>(AllocSrcOpnd(*srcOpnd));
404 CHECK_NULL_FATAL(regOpnd);
405 listOpndsNew->PushOpnd(*regOpnd);
406 }
407 insn.SetOperand(idx, *listOpndsNew);
408 }
409
AllocHandleSrc(Insn & insn,Operand & opnd,uint32 idx)410 void DefaultO0RegAllocator::AllocHandleSrc(Insn &insn, Operand &opnd, uint32 idx)
411 {
412 if (allocatedSet.find(&opnd) != allocatedSet.end() && opnd.IsRegister()) {
413 auto *regOpnd = &static_cast<RegOperand &>(opnd);
414 regno_t reg = regMap[regOpnd->GetRegisterNumber()];
415 availRegSet[reg] = false;
416 (void)liveReg.insert(reg); /* this register is live now */
417 insn.SetOperand(idx, cgFunc->GetOpndBuilder()->CreatePReg(reg, regOpnd->GetSize(), regOpnd->GetRegisterType()));
418 } else {
419 Operand *srcOpnd = AllocSrcOpnd(opnd);
420 CHECK_NULL_FATAL(srcOpnd);
421 insn.SetOperand(idx, *srcOpnd);
422 }
423 }
424
AllocateRegisters()425 bool DefaultO0RegAllocator::AllocateRegisters()
426 {
427 InitAvailReg();
428 cgFunc->SetIsAfterRegAlloc();
429
430 FOR_ALL_BB_REV(bb, cgFunc) {
431 if (bb->IsEmpty()) {
432 continue;
433 }
434
435 SetupRegLiveness(bb);
436 FOR_BB_INSNS_REV(insn, bb) {
437 if (!insn->IsMachineInstruction()) {
438 continue;
439 }
440
441 /* handle inline assembly first due to specific def&use order */
442 if (insn->IsAsmInsn()) {
443 AllocHandleDestList(*insn, insn->GetOperand(kAsmClobberListOpnd), kAsmClobberListOpnd);
444 AllocHandleDestList(*insn, insn->GetOperand(kAsmOutputListOpnd), kAsmOutputListOpnd);
445 AllocHandleSrcList(*insn, insn->GetOperand(kAsmInputListOpnd), kAsmInputListOpnd);
446 }
447
448 const InsnDesc *curMd = insn->GetDesc();
449
450 for (uint32 i = 0; i < insn->GetOperandSize() && !insn->IsAsmInsn(); ++i) { /* the dest registers */
451 Operand &opnd = insn->GetOperand(i);
452 if (!(opnd.IsRegister() && curMd->GetOpndDes(i)->IsDef())) {
453 continue;
454 }
455 if (opnd.IsList()) {
456 AllocHandleDestList(*insn, opnd, i);
457 } else {
458 AllocHandleDest(*insn, opnd, i);
459 }
460 }
461
462 for (uint32 i = 0; i < insn->GetOperandSize() && !insn->IsAsmInsn(); ++i) { /* the src registers */
463 Operand &opnd = insn->GetOperand(i);
464 if (!((opnd.IsRegister() && curMd->GetOpndDes(i)->IsUse()) || opnd.IsMemoryAccessOperand())) {
465 continue;
466 }
467 if (opnd.IsList()) {
468 AllocHandleSrcList(*insn, opnd, i);
469 } else {
470 AllocHandleSrc(*insn, opnd, i);
471 }
472 }
473
474 /* hack. a better way to handle intrinsics? */
475 for (auto rememberReg : rememberRegs) {
476 DEBUG_ASSERT(rememberReg != regInfo->GetInvalidReg(), "not a valid register");
477 ReleaseReg(rememberReg);
478 }
479 rememberRegs.clear();
480 }
481 }
482 /*
483 * we store both FP/LR if using FP or if not using FP, but func has a call
484 * Using FP, record it for saving
485 * notice the order here : the first callee saved reg is expected to be RFP.
486 */
487 regInfo->Fini();
488 regInfo->SaveCalleeSavedReg(calleeSaveUsed);
489 return true;
490 }
491 } /* namespace maplebe */
492