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 ®Opnd) 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 ®Opnd = 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 ®Dest = static_cast<RegOperand &>(insn->GetOperand(kInsnFirstOpnd));
203 RegOperand ®Src = 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 ®Dest, RegOperand ®Src)
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 ®Dest = static_cast<RegOperand &>(insn->GetOperand(kInsnFirstOpnd));
288 auto ®Src = 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 ®Dest = static_cast<RegOperand &>(insn->GetOperand(kInsnFirstOpnd));
308 RegOperand ®Src = 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