1 /**
2 * Copyright (c) 2021-2022 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_resolver.h"
17 #include "reg_type.h"
18 #include "compiler/optimizer/ir/inst.h"
19 #include "compiler/optimizer/ir/graph.h"
20 #include "compiler/optimizer/ir/basicblock.h"
21 #include "compiler/optimizer/analysis/dominators_tree.h"
22 #include "optimizer/analysis/loop_analyzer.h"
23
24 namespace panda::compiler {
25 /*
26 * For each instruction set destination register if it is assigned,
27 * Pop inputs from stack and push result on stack if stack slot is assigned.
28 */
Resolve()29 void RegAllocResolver::Resolve()
30 {
31 // We use RPO order because we need to calculate Caller's roots mask before its inlined callees
32 // (see PropagateCallerMasks).
33 for (auto block : GetGraph()->GetBlocksRPO()) {
34 for (auto inst : block->AllInstsSafe()) {
35 if (inst->IsSaveState()) {
36 continue;
37 }
38 ResolveInputs(inst);
39 ResolveOutput(inst);
40 if (GetGraph()->IsInstThrowable(inst)) {
41 AddCatchPhiMoves(inst);
42 }
43 }
44 }
45 }
46
AddCatchPhiMoves(Inst * inst)47 void RegAllocResolver::AddCatchPhiMoves(Inst *inst)
48 {
49 auto spill_fill_inst = GetGraph()->CreateInstSpillFill();
50 spill_fill_inst->SetSpillFillType(SpillFillType::INPUT_FILL);
51 auto handlers = GetGraph()->GetThrowableInstHandlers(inst);
52
53 for (auto catch_handler : handlers) {
54 for (auto catch_inst : catch_handler->AllInsts()) {
55 if (!catch_inst->IsCatchPhi() || catch_inst->CastToCatchPhi()->IsAcc()) {
56 continue;
57 }
58 auto catch_phi = catch_inst->CastToCatchPhi();
59 const auto &throwable_insts = catch_phi->GetThrowableInsts();
60 auto it = std::find(throwable_insts->begin(), throwable_insts->end(), inst);
61 if (it == throwable_insts->end()) {
62 continue;
63 }
64 int index = std::distance(throwable_insts->begin(), it);
65 auto catch_input = catch_phi->GetDataFlowInput(index);
66 auto input_interval = liveness_->GetInstLifeIntervals(catch_input);
67 ASSERT(input_interval->GetSibling() == nullptr);
68 auto catch_phi_interval = liveness_->GetInstLifeIntervals(catch_phi);
69 if (input_interval->GetLocation() != catch_phi_interval->GetLocation()) {
70 ConnectIntervals(spill_fill_inst, input_interval, catch_phi_interval);
71 }
72 }
73 }
74 if (!spill_fill_inst->GetSpillFills().empty()) {
75 inst->InsertBefore(spill_fill_inst);
76 }
77 }
78
ResolveInputs(Inst * inst)79 void RegAllocResolver::ResolveInputs(Inst *inst)
80 {
81 if (inst->IsPhi() || inst->IsCatchPhi()) {
82 return;
83 }
84
85 // Life-position before instruction to analyze intervals, that were splited directly before it
86 auto pre_ins_ln = liveness_->GetInstLifeIntervals(inst)->GetBegin() - 1U;
87
88 for (size_t i = 0; i < inst->GetInputsCount(); i++) {
89 auto location = inst->GetLocation(i);
90 auto input_interval = liveness_->GetInstLifeIntervals(inst->GetDataFlowInput(i));
91 if (CanReadFromAccumulator(inst, i) || input_interval->NoDest() || location.IsInvalid()) {
92 continue;
93 }
94
95 // Interval with fixed register can be splited before `inst`: we don't need any extra moves in that case,
96 // since fixed register can't be overwrite
97 auto sibling = input_interval->FindSiblingAt(pre_ins_ln);
98 ASSERT(sibling != nullptr);
99 if (location.IsFixedRegister() && sibling->GetLocation() == location) {
100 continue;
101 }
102
103 // Otherwise use sibling covering `inst`
104 if (sibling->GetEnd() == pre_ins_ln) {
105 sibling = sibling->GetSibling();
106 }
107
108 // Input's location required any register: specify the allocated one
109 if (location.IsUnallocatedRegister()) {
110 ASSERT(sibling->HasReg());
111 inst->SetLocation(i, sibling->GetLocation());
112 continue;
113 }
114
115 // Finally, if input's location is not equal to the required one, add spill-fill
116 if (sibling->GetLocation() != location) {
117 AddMoveToFixedLocation(inst, sibling->GetLocation(), i);
118 }
119 }
120 }
121
AddMoveToFixedLocation(Inst * inst,Location input_location,size_t input_num)122 void RegAllocResolver::AddMoveToFixedLocation(Inst *inst, Location input_location, size_t input_num)
123 {
124 // Create or get existing SpillFillInst
125 SpillFillInst *sf_inst {};
126 if (inst->GetPrev() != nullptr && inst->GetPrev()->IsSpillFill()) {
127 sf_inst = inst->GetPrev()->CastToSpillFill();
128 } else {
129 sf_inst = GetGraph()->CreateInstSpillFill();
130 sf_inst->SetSpillFillType(SpillFillType::INPUT_FILL);
131 inst->InsertBefore(sf_inst);
132 }
133
134 // Add move from input to fixed location
135 auto type = ConvertRegType(GetGraph(), inst->GetInputType(input_num));
136 auto fixed_location = inst->GetLocation(input_num);
137 if (fixed_location.IsFixedRegister()) {
138 GetGraph()->SetRegUsage(fixed_location.GetValue(), type);
139 }
140 sf_inst->AddSpillFill(input_location, fixed_location, type);
141 }
142
GetFirstUserOrInst(Inst * inst)143 Inst *GetFirstUserOrInst(Inst *inst)
144 {
145 if (!inst->GetUsers().Empty()) {
146 return inst->GetFirstUser()->GetInst();
147 }
148 return inst;
149 }
150
151 // For implicit null check we need to find the first null check's user to
152 // correctly capture SaveState's input locations, because implicit null checks are fired
153 // when its input is accessed by its users (for example, when LoadArray instruction is loading
154 // value from null array reference). Some life intervals may change its location (due to spilling)
155 // between NullCheck and its users, so locations captured at implicit null check could be incorrect.
156 // While implicit NullCheck may have multiple users we can use only a user dominating all other users,
157 // because null check either will be fired at it, or won't be fired at all.
GetExplicitUser(Inst * inst)158 Inst *GetExplicitUser(Inst *inst)
159 {
160 if (inst->GetUsers().Empty()) {
161 return inst;
162 }
163 if (inst->HasSingleUser()) {
164 return inst->GetUsers().Front().GetInst();
165 }
166
167 Inst *user_inst {nullptr};
168 #ifndef NDEBUG
169 for (auto &user : inst->GetUsers()) {
170 if (user.GetInst()->IsPhi()) {
171 continue;
172 }
173 ASSERT(user_inst != nullptr && user_inst->IsDominate(user.GetInst()));
174 }
175 #endif
176 return user_inst;
177 }
178
PropagateCallerMasks(SaveStateInst * save_state)179 void RegAllocResolver::PropagateCallerMasks(SaveStateInst *save_state)
180 {
181 save_state->CreateRootsStackMask(GetGraph()->GetAllocator());
182 auto user = GetExplicitUser(GetFirstUserOrInst(save_state));
183 // Get location of save state inputs at the save state user (note that at this point
184 // all inputs will have the same location at all users (excluding ReturnInlined that should be skipped)).
185 FillSaveStateRootsMask(save_state, user, save_state);
186 }
187
FillSaveStateRootsMask(SaveStateInst * save_state,Inst * user,SaveStateInst * target_ss)188 void RegAllocResolver::FillSaveStateRootsMask(SaveStateInst *save_state, Inst *user, SaveStateInst *target_ss)
189 {
190 auto dst_ln = liveness_->GetInstLifeIntervals(user)->GetBegin();
191
192 for (size_t i = 0; i < save_state->GetInputsCount(); ++i) {
193 auto input_inst = save_state->GetDataFlowInput(i);
194 if (input_inst->IsConst() || !IsTypeCollectable(input_inst->GetType())) {
195 continue;
196 }
197 auto input_interval = liveness_->GetInstLifeIntervals(input_inst);
198 auto sibling = input_interval->FindSiblingAt(dst_ln);
199 ASSERT(sibling != nullptr);
200 if (!sibling->SplitCover(dst_ln)) {
201 continue;
202 }
203 AddLocationToRoots(sibling->GetLocation(), target_ss, GetGraph());
204 #ifndef NDEBUG
205 for (auto &test_user : target_ss->GetUsers()) {
206 if (test_user.GetInst()->GetId() == user->GetId()) {
207 continue;
208 }
209 auto explicit_test_user = GetExplicitUser(test_user.GetInst());
210 auto udst_ln = liveness_->GetInstLifeIntervals(explicit_test_user)->GetBegin();
211 ASSERT(sibling->GetLocation() == input_interval->FindSiblingAt(udst_ln)->GetLocation());
212 }
213 #endif
214 }
215 }
216
217 /*
218 * Pop output on stack from reserved register
219 */
ResolveOutput(Inst * inst)220 void RegAllocResolver::ResolveOutput(Inst *inst)
221 {
222 // Multi-output instructions' dst registers will be filled after procecssing theirs pseudo users
223 if (inst->GetLinearNumber() == INVALID_LINEAR_NUM || inst->GetDstCount() > 1) {
224 return;
225 }
226
227 if (CanStoreToAccumulator(inst)) {
228 return;
229 }
230
231 auto inst_interval = liveness_->GetInstLifeIntervals(inst);
232 if (inst_interval->NoDest()) {
233 inst->SetDstReg(INVALID_REG);
234 return;
235 }
236
237 if (inst->GetOpcode() == Opcode::Parameter) {
238 inst->CastToParameter()->GetLocationData().SetDst(inst_interval->GetLocation());
239 }
240 // Process multi-output inst
241 size_t dst_mum = inst->GetSrcRegIndex();
242 // Wrtie dst
243 auto reg_type = inst_interval->GetType();
244 if (inst_interval->HasReg()) {
245 auto reg = inst_interval->GetReg();
246 inst->SetDstReg(dst_mum, reg);
247 GetGraph()->SetRegUsage(reg, reg_type);
248 } else {
249 ASSERT(inst->IsConst() || inst->IsPhi() || inst->IsParameter());
250 }
251 }
252
ResolveCatchPhis()253 bool RegAllocResolver::ResolveCatchPhis()
254 {
255 for (auto block : GetGraph()->GetBlocksRPO()) {
256 if (!block->IsCatchBegin()) {
257 continue;
258 }
259 for (auto inst : block->AllInstsSafe()) {
260 if (!inst->IsCatchPhi()) {
261 break;
262 }
263 if (inst->CastToCatchPhi()->IsAcc()) {
264 continue;
265 }
266 // This is the case when all throwable instructions were removed from the try-block,
267 // so that catch-handler is unreachable
268 if (inst->GetInputs().Empty()) {
269 return false;
270 }
271 auto new_catch_phi = SqueezeCatchPhiInputs(inst->CastToCatchPhi());
272 if (new_catch_phi != nullptr) {
273 inst->ReplaceUsers(new_catch_phi);
274 block->RemoveInst(inst);
275 }
276 }
277 }
278 return true;
279 }
280
281 /**
282 * Try to remove catch phi's inputs:
283 * If the input's corresponding throwable instruction dominates other throwable inst, we can remove other equal catch
284 * phi's input
285 *
286 * CatchPhi(v1, v1, v1, v2, v2, v2) -> CatchPhi(v1, v2)
287 *
288 * Return nullptr if inputs count was not reduced.
289 */
SqueezeCatchPhiInputs(CatchPhiInst * catch_phi)290 Inst *RegAllocResolver::SqueezeCatchPhiInputs(CatchPhiInst *catch_phi)
291 {
292 bool inputs_are_identical = true;
293 auto first_input = catch_phi->GetInput(0).GetInst();
294 for (size_t i = 1; i < catch_phi->GetInputsCount(); ++i) {
295 if (catch_phi->GetInput(i).GetInst() != first_input) {
296 inputs_are_identical = false;
297 break;
298 }
299 }
300 if (inputs_are_identical) {
301 return first_input;
302 }
303
304 // Create a new one and fill it with the necessary inputs
305 auto new_catch_phi = GetGraph()->CreateInstCatchPhi(catch_phi->GetType(), catch_phi->GetPc());
306 ASSERT(catch_phi->GetBasicBlock()->GetFirstInst()->IsCatchPhi());
307 catch_phi->GetBasicBlock()->PrependInst(new_catch_phi);
308 for (size_t i = 0; i < catch_phi->GetInputsCount(); i++) {
309 auto input_inst = catch_phi->GetInput(i).GetInst();
310 auto current_throwable_inst = catch_phi->GetThrowableInst(i);
311 ASSERT(GetGraph()->IsInstThrowable(current_throwable_inst));
312 bool skip = false;
313 for (size_t j = 0; j < new_catch_phi->GetInputsCount(); j++) {
314 auto saved_inst = new_catch_phi->GetInput(j).GetInst();
315 if (saved_inst != input_inst) {
316 continue;
317 }
318 auto saved_throwable_inst = new_catch_phi->GetThrowableInst(j);
319 if (saved_throwable_inst->IsDominate(current_throwable_inst)) {
320 skip = true;
321 }
322 if (current_throwable_inst->IsDominate(saved_throwable_inst)) {
323 new_catch_phi->ReplaceThrowableInst(saved_throwable_inst, current_throwable_inst);
324 skip = true;
325 }
326 if (skip) {
327 break;
328 }
329 }
330 if (!skip) {
331 new_catch_phi->AppendInput(input_inst);
332 new_catch_phi->AppendThrowableInst(current_throwable_inst);
333 }
334 }
335 if (new_catch_phi->GetInputsCount() == catch_phi->GetInputsCount()) {
336 new_catch_phi->GetBasicBlock()->RemoveInst(new_catch_phi);
337 return nullptr;
338 }
339 return new_catch_phi;
340 }
341
342 } // namespace panda::compiler
343