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