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