• 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_reg_coalesce.h"
17 #include "cg.h"
18 #include "cg_option.h"
19 #include "aarch64_isa.h"
20 #include "aarch64_insn.h"
21 #include "aarch64_cgfunc.h"
22 #include "aarch64_cg.h"
23 
24 /*
25  * This phase implements if-conversion optimization,
26  * which tries to convert conditional branches into cset/csel instructions
27  */
28 namespace maplebe {
29 
30 #define REGCOAL_DUMP CG_DEBUG_FUNC(*cgFunc)
31 
IsUnconcernedReg(const RegOperand & regOpnd) const32 bool AArch64LiveIntervalAnalysis::IsUnconcernedReg(const RegOperand &regOpnd) const
33 {
34     RegType regType = regOpnd.GetRegisterType();
35     if (regType == kRegTyCc || regType == kRegTyVary) {
36         return true;
37     }
38     if (regOpnd.GetRegisterNumber() == RZR) {
39         return true;
40     }
41     if (!regOpnd.IsVirtualRegister()) {
42         return true;
43     }
44     return false;
45 }
46 
GetOrCreateLiveInterval(regno_t regNO)47 LiveInterval *AArch64LiveIntervalAnalysis::GetOrCreateLiveInterval(regno_t regNO)
48 {
49     LiveInterval *lr = GetLiveInterval(regNO);
50     if (lr == nullptr) {
51         lr = memPool->New<LiveInterval>(alloc);
52         vregIntervals[regNO] = lr;
53         lr->SetRegNO(regNO);
54     }
55     return lr;
56 }
57 
UpdateCallInfo()58 void AArch64LiveIntervalAnalysis::UpdateCallInfo()
59 {
60     for (auto vregNO : vregLive) {
61         LiveInterval *lr = GetLiveInterval(vregNO);
62         if (lr == nullptr) {
63             return;
64         }
65         lr->IncNumCall();
66     }
67 }
68 
SetupLiveIntervalByOp(Operand & op,Insn & insn,bool isDef)69 void AArch64LiveIntervalAnalysis::SetupLiveIntervalByOp(Operand &op, Insn &insn, bool isDef)
70 {
71     if (!op.IsRegister()) {
72         return;
73     }
74     auto &regOpnd = static_cast<RegOperand &>(op);
75     uint32 regNO = regOpnd.GetRegisterNumber();
76     if (IsUnconcernedReg(regOpnd)) {
77         return;
78     }
79     LiveInterval *lr = GetOrCreateLiveInterval(regNO);
80     CHECK_FATAL(insn.GetId() >= 1, "value overflow");
81     uint32 point = isDef ? insn.GetId() : (insn.GetId() - 1);
82     lr->AddRange(insn.GetBB()->GetId(), point, vregLive.find(regNO) != vregLive.end());
83     if (lr->GetRegType() == kRegTyUndef) {
84         lr->SetRegType(regOpnd.GetRegisterType());
85     }
86     if (candidates.find(regNO) != candidates.end()) {
87         lr->AddRefPoint(&insn, isDef);
88     }
89     if (isDef) {
90         vregLive.erase(regNO);
91     } else {
92         vregLive.insert(regNO);
93     }
94 }
95 
ComputeLiveIntervalsForEachDefOperand(Insn & insn)96 void AArch64LiveIntervalAnalysis::ComputeLiveIntervalsForEachDefOperand(Insn &insn)
97 {
98     const InsnDesc *md = insn.GetDesc();
99     uint32 opndNum = insn.GetOperandSize();
100     for (uint32 i = 0; i < opndNum; ++i) {
101         if (insn.GetMachineOpcode() == MOP_asm && (i == kAsmOutputListOpnd || i == kAsmClobberListOpnd)) {
102             for (auto opnd : static_cast<ListOperand &>(insn.GetOperand(i)).GetOperands()) {
103                 SetupLiveIntervalByOp(*static_cast<RegOperand *>(opnd), insn, true);
104             }
105             continue;
106         }
107         Operand &opnd = insn.GetOperand(i);
108         if (opnd.IsMemoryAccessOperand()) {
109             auto &memOpnd = static_cast<MemOperand &>(opnd);
110             if (!memOpnd.IsIntactIndexed()) {
111                 SetupLiveIntervalByOp(opnd, insn, true);
112             }
113         }
114         if (!md->GetOpndDes(i)->IsRegDef()) {
115             continue;
116         }
117         SetupLiveIntervalByOp(opnd, insn, true);
118         auto *drivedRef = static_cast<RegOperand&>(opnd).GetBaseRefOpnd();
119         if (drivedRef != nullptr) {
120             SetupLiveIntervalByOp(*drivedRef, insn, false);
121         }
122     }
123 }
124 
ComputeLiveIntervalsForEachUseOperand(Insn & insn)125 void AArch64LiveIntervalAnalysis::ComputeLiveIntervalsForEachUseOperand(Insn &insn)
126 {
127     const InsnDesc *md = insn.GetDesc();
128     uint32 opndNum = insn.GetOperandSize();
129     for (uint32 i = 0; i < opndNum; ++i) {
130         if (insn.GetMachineOpcode() == MOP_asm && i == kAsmInputListOpnd) {
131             for (auto opnd : static_cast<ListOperand &>(insn.GetOperand(i)).GetOperands()) {
132                 SetupLiveIntervalByOp(*static_cast<RegOperand *>(opnd), insn, false);
133             }
134             continue;
135         }
136         if (md->GetOpndDes(i)->IsRegDef() && !md->GetOpndDes(i)->IsRegUse()) {
137             continue;
138         }
139         Operand &opnd = insn.GetOperand(i);
140         if (opnd.IsList()) {
141             auto &listOpnd = static_cast<ListOperand &>(opnd);
142             for (auto op : listOpnd.GetOperands()) {
143                 SetupLiveIntervalByOp(*op, insn, false);
144             }
145         } else if (opnd.IsMemoryAccessOperand()) {
146             auto &memOpnd = static_cast<MemOperand &>(opnd);
147             Operand *base = memOpnd.GetBaseRegister();
148             Operand *offset = memOpnd.GetIndexRegister();
149             if (base != nullptr) {
150                 SetupLiveIntervalByOp(*base, insn, false);
151             }
152             if (offset != nullptr) {
153                 SetupLiveIntervalByOp(*offset, insn, false);
154             }
155         } else if (opnd.IsPhi()) {
156             auto &phiOpnd = static_cast<PhiOperand &>(opnd);
157             for (auto opIt : phiOpnd.GetOperands()) {
158                 SetupLiveIntervalByOp(*opIt.second, insn, false);
159             }
160         } else if (opnd.IsRegister()) {
161             SetupLiveIntervalByOp(opnd, insn, false);
162             auto *drivedRef = static_cast<RegOperand&>(opnd).GetBaseRefOpnd();
163             if (drivedRef != nullptr) {
164                 SetupLiveIntervalByOp(*drivedRef, insn, false);
165             }
166         }
167     }
168 
169     if (insn.GetStackMap() != nullptr) {
170         for (auto [_, opnd] : insn.GetStackMap()->GetDeoptInfo().GetDeoptBundleInfo()) {
171             SetupLiveIntervalByOp(*opnd, insn, false);
172         }
173     }
174 }
175 
176 /* handle live range for bb->live_out */
SetupLiveIntervalInLiveOut(regno_t liveOut,const BB & bb,uint32 currPoint)177 void AArch64LiveIntervalAnalysis::SetupLiveIntervalInLiveOut(regno_t liveOut, const BB &bb, uint32 currPoint)
178 {
179     --currPoint;
180 
181     if (liveOut >= kAllRegNum) {
182         (void)vregLive.insert(liveOut);
183         LiveInterval *lr = GetOrCreateLiveInterval(liveOut);
184         if (lr == nullptr) {
185             return;
186         }
187         lr->AddRange(bb.GetId(), currPoint, false);
188         return;
189     }
190 }
191 
CollectCandidate()192 void AArch64LiveIntervalAnalysis::CollectCandidate()
193 {
194     for (size_t bbIdx = bfs->sortedBBs.size(); bbIdx > 0; --bbIdx) {
195         BB *bb = bfs->sortedBBs[bbIdx - 1];
196 
197         FOR_BB_INSNS_SAFE(insn, bb, ninsn) {
198             if (!insn->IsMachineInstruction()) {
199                 continue;
200             }
201             if (IsRegistersCopy(*insn)) {
202                 RegOperand &regDest = static_cast<RegOperand &>(insn->GetOperand(kInsnFirstOpnd));
203                 RegOperand &regSrc = static_cast<RegOperand &>(insn->GetOperand(kInsnSecondOpnd));
204                 if (regDest.GetRegisterNumber() == regSrc.GetRegisterNumber()) {
205                     continue;
206                 }
207                 if (regDest.IsVirtualRegister()) {
208                     candidates.insert(regDest.GetRegisterNumber());
209                 }
210                 if (regSrc.IsVirtualRegister()) {
211                     candidates.insert(regSrc.GetRegisterNumber());
212                 }
213             }
214         }
215     }
216 }
217 
IsRegistersCopy(Insn & insn)218 bool AArch64LiveIntervalAnalysis::IsRegistersCopy(Insn &insn)
219 {
220     MOperator mOp = insn.GetMachineOpcode();
221     if (mOp == MOP_xmovrr || mOp == MOP_wmovrr || mOp == MOP_xvmovs || mOp == MOP_xvmovd) {
222         return true;
223     }
224     return false;
225 }
226 
CheckInterference(LiveInterval & li1,LiveInterval & li2) const227 void AArch64LiveIntervalAnalysis::CheckInterference(LiveInterval &li1, LiveInterval &li2) const
228 {
229     auto ranges1 = li1.GetRanges();
230     auto ranges2 = li2.GetRanges();
231     bool conflict = false;
232     for (auto range : ranges1) {
233         auto bbid = range.first;
234         auto posVec1 = range.second;
235         auto it = ranges2.find(bbid);
236         if (it == ranges2.end()) {
237             continue;
238         } else {
239             /* check overlap */
240             auto posVec2 = it->second;
241             for (auto pos1 : posVec1) {
242                 for (auto pos2 : posVec2) {
243                     if (!((pos1.first < pos2.first && pos1.second < pos2.first) ||
244                           (pos2.first < pos1.second && pos2.second < pos1.first))) {
245                         conflict = true;
246                         break;
247                     }
248                 }
249             }
250         }
251     }
252     if (conflict) {
253         li1.AddConflict(li2.GetRegNO());
254         li2.AddConflict(li1.GetRegNO());
255     }
256     return;
257 }
258 
259 /* replace regDest with regSrc. */
CoalesceRegPair(RegOperand & regDest,RegOperand & regSrc)260 void AArch64LiveIntervalAnalysis::CoalesceRegPair(RegOperand &regDest, RegOperand &regSrc)
261 {
262     LiveInterval *lrDest = GetLiveInterval(regDest.GetRegisterNumber());
263     if (lrDest == nullptr) {
264         return;
265     }
266     LiveInterval *lrSrc = GetLiveInterval(regSrc.GetRegisterNumber());
267     regno_t destNO = regDest.GetRegisterNumber();
268     /* replace all refPoints */
269     for (auto insn : lrDest->GetDefPoint()) {
270         cgFunc->ReplaceOpndInInsn(regDest, regSrc, *insn, destNO);
271     }
272     for (auto insn : lrDest->GetUsePoint()) {
273         cgFunc->ReplaceOpndInInsn(regDest, regSrc, *insn, destNO);
274     }
275 
276     DEBUG_ASSERT(lrDest && lrSrc, "get live interval failed");
277     CoalesceLiveIntervals(*lrDest, *lrSrc);
278 }
279 
CollectMoveForEachBB(BB & bb,std::vector<Insn * > & movInsns) const280 void AArch64LiveIntervalAnalysis::CollectMoveForEachBB(BB &bb, std::vector<Insn *> &movInsns) const
281 {
282     FOR_BB_INSNS_SAFE(insn, &bb, ninsn) {
283         if (!insn->IsMachineInstruction()) {
284             continue;
285         }
286         if (IsRegistersCopy(*insn)) {
287             auto &regDest = static_cast<RegOperand &>(insn->GetOperand(kInsnFirstOpnd));
288             auto &regSrc = static_cast<RegOperand &>(insn->GetOperand(kInsnSecondOpnd));
289             if (!regSrc.IsVirtualRegister() || !regDest.IsVirtualRegister()) {
290                 continue;
291             }
292             if (regSrc.GetRegisterNumber() == regDest.GetRegisterNumber()) {
293                 continue;
294             }
295             movInsns.emplace_back(insn);
296         }
297     }
298 }
299 
CoalesceMoves(std::vector<Insn * > & movInsns,bool phiOnly)300 void AArch64LiveIntervalAnalysis::CoalesceMoves(std::vector<Insn *> &movInsns, bool phiOnly)
301 {
302     AArch64CGFunc *a64CGFunc = static_cast<AArch64CGFunc *>(cgFunc);
303     bool changed = false;
304     do {
305         changed = false;
306         for (auto insn : movInsns) {
307             RegOperand &regDest = static_cast<RegOperand &>(insn->GetOperand(kInsnFirstOpnd));
308             RegOperand &regSrc = static_cast<RegOperand &>(insn->GetOperand(kInsnSecondOpnd));
309             if (regSrc.GetRegisterNumber() == regDest.GetRegisterNumber()) {
310                 continue;
311             }
312             if (!insn->IsPhiMovInsn() && phiOnly) {
313                 continue;
314             }
315             if (a64CGFunc->IsRegRematCand(regDest) != a64CGFunc->IsRegRematCand(regSrc)) {
316                 if (insn->IsPhiMovInsn()) {
317                     a64CGFunc->ClearRegRematInfo(regDest);
318                     a64CGFunc->ClearRegRematInfo(regSrc);
319                 } else {
320                     continue;
321                 }
322             }
323             if (a64CGFunc->IsRegRematCand(regDest) && a64CGFunc->IsRegRematCand(regSrc) &&
324                 !a64CGFunc->IsRegSameRematInfo(regDest, regSrc)) {
325                 if (insn->IsPhiMovInsn()) {
326                     a64CGFunc->ClearRegRematInfo(regDest);
327                     a64CGFunc->ClearRegRematInfo(regSrc);
328                 } else {
329                     continue;
330                 }
331             }
332             LiveInterval *li1 = GetLiveInterval(regDest.GetRegisterNumber());
333             LiveInterval *li2 = GetLiveInterval(regSrc.GetRegisterNumber());
334             if (li1 == nullptr || li2 == nullptr) {
335                 return;
336             }
337             CheckInterference(*li1, *li2);
338             if (!li1->IsConflictWith(regSrc.GetRegisterNumber()) ||
339                 (li1->GetDefPoint().size() == 1 && li2->GetDefPoint().size() == 1)) {
340                 if (REGCOAL_DUMP) {
341                     LogInfo::MapleLogger() << "try to coalesce: " << regDest.GetRegisterNumber() << " <- "
342                                            << regSrc.GetRegisterNumber() << std::endl;
343                 }
344                 CoalesceRegPair(regDest, regSrc);
345                 changed = true;
346             } else {
347                 if (insn->IsPhiMovInsn() && phiOnly && REGCOAL_DUMP) {
348                     LogInfo::MapleLogger() << "fail to coalesce: " << regDest.GetRegisterNumber() << " <- "
349                                            << regSrc.GetRegisterNumber() << std::endl;
350                 }
351             }
352         }
353     } while (changed);
354 }
355 
CoalesceRegisters()356 void AArch64LiveIntervalAnalysis::CoalesceRegisters()
357 {
358     std::vector<Insn *> movInsns;
359     AArch64CGFunc *a64CGFunc = static_cast<AArch64CGFunc *>(cgFunc);
360     if (REGCOAL_DUMP) {
361         cgFunc->DumpCFGToDot("regcoal-");
362         LogInfo::MapleLogger() << "handle function: " << a64CGFunc->GetFunction().GetName() << std::endl;
363     }
364     for (size_t bbIdx = bfs->sortedBBs.size(); bbIdx > 0; --bbIdx) {
365         BB *bb = bfs->sortedBBs[bbIdx - 1];
366 
367         if (!bb->GetCritical()) {
368             continue;
369         }
370         CollectMoveForEachBB(*bb, movInsns);
371     }
372     for (size_t bbIdx = bfs->sortedBBs.size(); bbIdx > 0; --bbIdx) {
373         BB *bb = bfs->sortedBBs[bbIdx - 1];
374 
375         if (bb->GetCritical()) {
376             continue;
377         }
378         CollectMoveForEachBB(*bb, movInsns);
379     }
380 
381     bool coalescePhiOnly = (CGOptions::GetInstance().GetOptimizeLevel() != CGOptions::kLevelLiteCG);
382     CoalesceMoves(movInsns, coalescePhiOnly);
383 
384     /* clean up dead mov */
385     a64CGFunc->CleanupDeadMov(REGCOAL_DUMP);
386 }
387 
388 } /* namespace maplebe */
389