• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/code_generator/codegen.h"
19 #include "compiler/optimizer/ir/inst.h"
20 #include "compiler/optimizer/ir/graph.h"
21 #include "compiler/optimizer/ir/basicblock.h"
22 #include "compiler/optimizer/analysis/dominators_tree.h"
23 #include "optimizer/analysis/loop_analyzer.h"
24 
25 namespace panda::compiler {
26 /*
27  * For each instruction set destination register if it is assigned,
28  * Pop inputs from stack and push result on stack if stack slot is assigned.
29  */
Resolve()30 void RegAllocResolver::Resolve()
31 {
32     // We use RPO order because we need to calculate Caller's roots mask before its inlined callees
33     // (see PropagateCallerMasks).
34     for (auto block : GetGraph()->GetBlocksRPO()) {
35         for (auto inst : block->AllInstsSafe()) {
36             if (inst->IsSaveState()) {
37                 ResolveSaveState(inst);
38                 continue;
39             }
40             ResolveInputs(inst);
41             ResolveOutput(inst);
42             if (GetGraph()->IsInstThrowable(inst)) {
43                 AddCatchPhiMoves(inst);
44             }
45         }
46     }
47 }
48 
AddCatchPhiMoves(Inst * inst)49 void RegAllocResolver::AddCatchPhiMoves(Inst *inst)
50 {
51     auto spill_fill_inst = GetGraph()->CreateInstSpillFill();
52     spill_fill_inst->SetSpillFillType(SpillFillType::INPUT_FILL);
53     auto handlers = GetGraph()->GetThrowableInstHandlers(inst);
54 
55     for (auto catch_handler : handlers) {
56         for (auto catch_inst : catch_handler->AllInsts()) {
57             if (!catch_inst->IsCatchPhi() || catch_inst->CastToCatchPhi()->IsAcc()) {
58                 continue;
59             }
60             auto catch_phi = catch_inst->CastToCatchPhi();
61             const auto &throwable_insts = catch_phi->GetThrowableInsts();
62             auto it = std::find(throwable_insts->begin(), throwable_insts->end(), inst);
63             if (it == throwable_insts->end()) {
64                 continue;
65             }
66             int index = std::distance(throwable_insts->begin(), it);
67             auto catch_input = catch_phi->GetDataFlowInput(index);
68             auto input_interval = liveness_->GetInstLifeIntervals(catch_input);
69             ASSERT(input_interval->GetSibling() == nullptr);
70             auto catch_phi_interval = liveness_->GetInstLifeIntervals(catch_phi);
71             if (input_interval->GetLocation() != catch_phi_interval->GetLocation()) {
72                 ConnectIntervals(spill_fill_inst, input_interval, catch_phi_interval);
73             }
74         }
75     }
76     if (!spill_fill_inst->GetSpillFills().empty()) {
77         inst->InsertBefore(spill_fill_inst);
78     }
79 }
80 
ResolveInputs(Inst * inst)81 void RegAllocResolver::ResolveInputs(Inst *inst)
82 {
83     if (inst->IsPhi() || inst->IsCatchPhi() || IsPseudoUserOfMultiOutput(inst)) {
84         return;
85     }
86 
87     // Life-position before instruction to analyze intervals, that were splited directly before it
88     auto pre_ins_ln = liveness_->GetInstLifeIntervals(inst)->GetBegin() - 1U;
89 
90     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
91         auto location = inst->GetLocation(i);
92         auto input_interval = liveness_->GetInstLifeIntervals(inst->GetDataFlowInput(i));
93 
94         if (CanReadFromAccumulator(inst, i) || input_interval->NoDest() || location.IsInvalid()) {
95             continue;
96         }
97 
98         // Interval with fixed register can be splited before `inst`: we don't need any extra moves in that case,
99         // since fixed register can't be overwrite
100         auto sibling = input_interval->FindSiblingAt(pre_ins_ln);
101         ASSERT(sibling != nullptr);
102         if (location.IsFixedRegister() && sibling->GetLocation() == location) {
103             continue;
104         }
105 
106         // Otherwise use sibling covering `inst`
107         if (sibling->GetEnd() == pre_ins_ln) {
108             sibling = sibling->GetSibling();
109         }
110 
111         // Input's location required any register: specify the allocated one
112         if (location.IsUnallocatedRegister()) {
113             ASSERT(sibling->HasReg());
114             inst->SetLocation(i, sibling->GetLocation());
115             continue;
116         }
117 
118         // Finally, if input's location is not equal to the required one, add spill-fill
119         if (sibling->GetLocation() != location) {
120             AddMoveToFixedLocation(inst, sibling->GetLocation(), i);
121         }
122     }
123 }
124 
AddMoveToFixedLocation(Inst * inst,Location input_location,size_t input_num)125 void RegAllocResolver::AddMoveToFixedLocation(Inst *inst, Location input_location, size_t input_num)
126 {
127     // Create or get existing SpillFillInst
128     SpillFillInst *sf_inst {};
129     if (inst->GetPrev() != nullptr && inst->GetPrev()->IsSpillFill()) {
130         sf_inst = inst->GetPrev()->CastToSpillFill();
131     } else {
132         sf_inst = GetGraph()->CreateInstSpillFill();
133         sf_inst->SetSpillFillType(SpillFillType::INPUT_FILL);
134         inst->InsertBefore(sf_inst);
135     }
136 
137     // Add move from input to fixed location
138     auto type = ConvertRegType(GetGraph(), inst->GetInputType(input_num));
139     auto fixed_location = inst->GetLocation(input_num);
140     if (fixed_location.IsFixedRegister()) {
141         GetGraph()->SetRegUsage(fixed_location.GetValue(), type);
142     }
143     sf_inst->AddSpillFill(input_location, fixed_location, type);
144 }
145 
GetFirstUserOrInst(Inst * inst)146 Inst *GetFirstUserOrInst(Inst *inst)
147 {
148     for (auto &user : inst->GetUsers()) {
149         if (user.GetInst()->GetOpcode() != Opcode::ReturnInlined) {
150             return user.GetInst();
151         }
152     }
153     return inst;
154 }
155 
156 // For implicit null check we need to find the first null check's user to
157 // correctly capture SaveState's input locations, because implicit null checks are fired
158 // when its input is accessed by its users (for example, when LoadArray instruction is loading
159 // value from null array reference). Some life intervals may change its location (due to spilling)
160 // between NullCheck and its users, so locations captured at implicit null check could be incorrect.
161 // While implicit NullCheck may have multiple users we can use only a user dominating all other users,
162 // because null check either will be fired at it, or won't be fired at all.
GetExplicitUser(Inst * inst)163 Inst *GetExplicitUser(Inst *inst)
164 {
165     if (!inst->IsNullCheck() || !inst->CastToNullCheck()->IsImplicit() || inst->GetUsers().Empty()) {
166         return inst;
167     }
168     if (inst->HasSingleUser()) {
169         return inst->GetUsers().Front().GetInst();
170     }
171 
172     Inst *user_inst {nullptr};
173     for (auto &user : inst->GetUsers()) {
174         auto curr_inst = user.GetInst();
175         if (!IsSuitableForImplicitNullCheck(curr_inst)) {
176             continue;
177         }
178         if (curr_inst->GetInput(0).GetInst() != inst) {
179             continue;
180         }
181         if (!curr_inst->CanThrow()) {
182             continue;
183         }
184         user_inst = curr_inst;
185         break;
186     }
187 #ifndef NDEBUG
188     for (auto &user : inst->GetUsers()) {
189         if (user.GetInst()->IsPhi()) {
190             continue;
191         }
192         ASSERT(user_inst != nullptr && user_inst->IsDominate(user.GetInst()));
193     }
194 #endif
195     return user_inst;
196 }
197 
PropagateCallerMasks(SaveStateInst * save_state)198 void RegAllocResolver::PropagateCallerMasks(SaveStateInst *save_state)
199 {
200     save_state->CreateRootsStackMask(GetGraph()->GetAllocator());
201     auto user = GetExplicitUser(GetFirstUserOrInst(save_state));
202     // Get location of save state inputs at the save state user (note that at this point
203     // all inputs will have the same location at all users (excluding ReturnInlined that should be skipped)).
204     FillSaveStateRootsMask(save_state, user, save_state);
205     for (auto caller_inst = save_state->GetCallerInst(); caller_inst != nullptr;
206          caller_inst = caller_inst->GetSaveState()->GetCallerInst()) {
207         auto caller_ss = caller_inst->GetSaveState();
208         FillSaveStateRootsMask(caller_ss, user, save_state);
209     }
210 }
211 
FillSaveStateRootsMask(SaveStateInst * save_state,Inst * user,SaveStateInst * target_ss)212 void RegAllocResolver::FillSaveStateRootsMask(SaveStateInst *save_state, Inst *user, SaveStateInst *target_ss)
213 {
214     auto dst_ln = liveness_->GetInstLifeIntervals(user)->GetBegin();
215 
216     for (size_t i = 0; i < save_state->GetInputsCount(); ++i) {
217         auto input_inst = save_state->GetDataFlowInput(i);
218         if (input_inst->IsConst() || !IsTypeCollectable(input_inst->GetType())) {
219             continue;
220         }
221         auto input_interval = liveness_->GetInstLifeIntervals(input_inst);
222         auto sibling = input_interval->FindSiblingAt(dst_ln);
223         ASSERT(sibling != nullptr);
224         if (!sibling->SplitCover(dst_ln)) {
225             continue;
226         }
227         AddLocationToRoots(sibling->GetLocation(), target_ss, GetGraph());
228 #ifndef NDEBUG
229         for (auto &test_user : target_ss->GetUsers()) {
230             if (test_user.GetInst()->GetOpcode() == Opcode::ReturnInlined ||
231                 test_user.GetInst()->GetId() == user->GetId()) {
232                 continue;
233             }
234             auto explicit_test_user = GetExplicitUser(test_user.GetInst());
235             auto udst_ln = liveness_->GetInstLifeIntervals(explicit_test_user)->GetBegin();
236             ASSERT(sibling->GetLocation() == input_interval->FindSiblingAt(udst_ln)->GetLocation());
237         }
238 #endif
239     }
240 }
241 
242 namespace {
CopySaveState(Graph * graph,SaveStateInst * inst)243 SaveStateInst *CopySaveState(Graph *graph, SaveStateInst *inst)
244 {
245     auto copy = static_cast<SaveStateInst *>(inst->Clone(graph));
246     ASSERT(copy->GetCallerInst() == inst->GetCallerInst());
247     for (size_t input_idx = 0; input_idx < inst->GetInputsCount(); input_idx++) {
248         copy->AppendInput(inst->GetInput(input_idx));
249         copy->SetVirtualRegister(input_idx, inst->GetVirtualRegister(input_idx));
250     }
251     copy->SetLinearNumber(inst->GetLinearNumber());
252     return copy;
253 }
254 
HasSameLocation(LifeIntervals * interval,LifeNumber pos1,LifeNumber pos2)255 bool HasSameLocation(LifeIntervals *interval, LifeNumber pos1, LifeNumber pos2)
256 {
257     auto sibling1 = interval->FindSiblingAt(pos1);
258     auto sibling2 = interval->FindSiblingAt(pos2);
259     ASSERT(sibling1 != nullptr);
260     ASSERT(sibling2 != nullptr);
261     return sibling1->SplitCover(pos1) && sibling1->SplitCover(pos2) &&
262            sibling1->GetLocation() == sibling2->GetLocation();
263 }
264 
SaveStateCopyRequired(Inst * inst,User * curr_user,User * prev_user,const LivenessAnalyzer * la)265 bool SaveStateCopyRequired(Inst *inst, User *curr_user, User *prev_user, const LivenessAnalyzer *la)
266 {
267     ASSERT(inst->IsSaveState());
268     auto curr_user_ln = la->GetInstLifeIntervals(GetExplicitUser(curr_user->GetInst()))->GetBegin();
269     auto prev_user_ln = la->GetInstLifeIntervals(GetExplicitUser(prev_user->GetInst()))->GetBegin();
270     bool need_copy = false;
271     // If current save state is part of inlined method then we have to check location for all
272     // parent save states.
273     for (auto ss = static_cast<SaveStateInst *>(inst); ss != nullptr && !need_copy;) {
274         for (size_t input_idx = 0; input_idx < ss->GetInputsCount() && !need_copy; input_idx++) {
275             auto input_interval = la->GetInstLifeIntervals(ss->GetDataFlowInput(input_idx));
276             need_copy = !HasSameLocation(input_interval, curr_user_ln, prev_user_ln);
277         }
278         auto caller = ss->GetCallerInst();
279         if (caller == nullptr) {
280             ss = nullptr;
281         } else {
282             ss = caller->GetSaveState();
283         }
284     }
285     return need_copy;
286 }
287 }  // namespace
288 
ResolveSaveState(Inst * inst)289 void RegAllocResolver::ResolveSaveState(Inst *inst)
290 {
291     if (GetGraph()->GetCallingConvention() == nullptr) {
292         return;
293     }
294     ASSERT(inst->IsSaveState());
295 
296     bool handled_all_users = inst->HasSingleUser() || !inst->HasUsers();
297     while (!handled_all_users) {
298         size_t copy_users = 0;
299         auto user_it = inst->GetUsers().begin();
300         User *prev_user = &*user_it;
301         ++user_it;
302         bool need_copy = false;
303 
304         // Find first user having different location for some of the save state inputs and use SaveState's
305         // copy for all preceding users.
306         for (; user_it != inst->GetUsers().end() && !need_copy; ++user_it, copy_users++) {
307             auto &curr_user = *user_it;
308             // ReturnInline's SaveState is required only for SaveState's inputs life range propagation,
309             // so it does not actually matter which interval will be actually used.
310             if (prev_user->GetInst()->GetOpcode() == Opcode::ReturnInlined) {
311                 prev_user = &*user_it;
312                 continue;
313             }
314             if (curr_user.GetInst()->GetOpcode() == Opcode::ReturnInlined) {
315                 continue;
316             }
317             need_copy = SaveStateCopyRequired(inst, &curr_user, prev_user, liveness_);
318             prev_user = &*user_it;
319         }
320         if (need_copy) {
321             auto copy = CopySaveState(GetGraph(), static_cast<SaveStateInst *>(inst));
322             // Replace original SaveState with the copy for first N users (N = `copy_users` ).
323             while (copy_users > 0) {
324                 auto user_inst = inst->GetUsers().Front().GetInst();
325                 user_inst->ReplaceInput(inst, copy);
326                 copy_users--;
327             }
328             inst->GetBasicBlock()->InsertAfter(copy, inst);
329             PropagateCallerMasks(copy);
330             handled_all_users = inst->HasSingleUser();
331         } else {
332             handled_all_users = !(user_it != inst->GetUsers().end());
333         }
334     }
335     // At this point inst either has single user or all its inputs have the same location at all users.
336     PropagateCallerMasks(static_cast<SaveStateInst *>(inst));
337 }
338 
339 /*
340  * Pop output on stack from reserved register
341  */
ResolveOutput(Inst * inst)342 void RegAllocResolver::ResolveOutput(Inst *inst)
343 {
344     // Don't process LiveOut, since it is instruction with pseudo destination
345     if (inst->GetOpcode() == Opcode::LiveOut) {
346         return;
347     }
348     // Multi-output instructions' dst registers will be filled after procecssing theirs pseudo users
349     if (inst->GetLinearNumber() == INVALID_LINEAR_NUM || inst->GetDstCount() > 1) {
350         return;
351     }
352 
353     if (CanStoreToAccumulator(inst)) {
354         return;
355     }
356 
357     auto inst_interval = liveness_->GetInstLifeIntervals(inst);
358     if (inst_interval->NoDest()) {
359         inst->SetDstReg(INVALID_REG);
360         return;
361     }
362 
363     if (inst->GetOpcode() == Opcode::Parameter) {
364         inst->CastToParameter()->GetLocationData().SetDst(inst_interval->GetLocation());
365     }
366     // Process multi-output inst
367     size_t dst_mum = inst->GetSrcRegIndex();
368     if (IsPseudoUserOfMultiOutput(inst)) {
369         inst = inst->GetInput(0).GetInst();
370     }
371     // Wrtie dst
372     auto reg_type = inst_interval->GetType();
373     if (inst_interval->HasReg()) {
374         auto reg = inst_interval->GetReg();
375         inst->SetDstReg(dst_mum, reg);
376         GetGraph()->SetRegUsage(reg, reg_type);
377     } else {
378         ASSERT(inst->IsConst() || inst->IsPhi() || inst->IsParameter());
379     }
380 }
381 
ResolveCatchPhis()382 bool RegAllocResolver::ResolveCatchPhis()
383 {
384     for (auto block : GetGraph()->GetBlocksRPO()) {
385         if (!block->IsCatchBegin()) {
386             continue;
387         }
388         for (auto inst : block->AllInstsSafe()) {
389             if (!inst->IsCatchPhi()) {
390                 break;
391             }
392             if (inst->CastToCatchPhi()->IsAcc()) {
393                 continue;
394             }
395             // This is the case when all throwable instructions were removed from the try-block,
396             // so that catch-handler is unreachable
397             if (inst->GetInputs().Empty()) {
398                 return false;
399             }
400             auto new_catch_phi = SqueezeCatchPhiInputs(inst->CastToCatchPhi());
401             if (new_catch_phi != nullptr) {
402                 inst->ReplaceUsers(new_catch_phi);
403                 block->RemoveInst(inst);
404             }
405         }
406     }
407     return true;
408 }
409 
410 /**
411  * Try to remove catch phi's inputs:
412  * If the input's corresponding throwable instruction dominates other throwable inst, we can remove other equal catch
413  * phi's input
414  *
415  * CatchPhi(v1, v1, v1, v2, v2, v2) -> CatchPhi(v1, v2)
416  *
417  * Return nullptr if inputs count was not reduced.
418  */
SqueezeCatchPhiInputs(CatchPhiInst * catch_phi)419 Inst *RegAllocResolver::SqueezeCatchPhiInputs(CatchPhiInst *catch_phi)
420 {
421     bool inputs_are_identical = true;
422     auto first_input = catch_phi->GetInput(0).GetInst();
423     for (size_t i = 1; i < catch_phi->GetInputsCount(); ++i) {
424         if (catch_phi->GetInput(i).GetInst() != first_input) {
425             inputs_are_identical = false;
426             break;
427         }
428     }
429     if (inputs_are_identical) {
430         return first_input;
431     }
432 
433     // Create a new one and fill it with the necessary inputs
434     auto new_catch_phi = GetGraph()->CreateInstCatchPhi(catch_phi->GetType(), catch_phi->GetPc());
435     ASSERT(catch_phi->GetBasicBlock()->GetFirstInst()->IsCatchPhi());
436     catch_phi->GetBasicBlock()->PrependInst(new_catch_phi);
437     for (size_t i = 0; i < catch_phi->GetInputsCount(); i++) {
438         auto input_inst = catch_phi->GetInput(i).GetInst();
439         auto current_throwable_inst = catch_phi->GetThrowableInst(i);
440         ASSERT(GetGraph()->IsInstThrowable(current_throwable_inst));
441         bool skip = false;
442         for (size_t j = 0; j < new_catch_phi->GetInputsCount(); j++) {
443             auto saved_inst = new_catch_phi->GetInput(j).GetInst();
444             if (saved_inst != input_inst) {
445                 continue;
446             }
447             auto saved_throwable_inst = new_catch_phi->GetThrowableInst(j);
448             if (saved_throwable_inst->IsDominate(current_throwable_inst)) {
449                 skip = true;
450             }
451             if (current_throwable_inst->IsDominate(saved_throwable_inst)) {
452                 new_catch_phi->ReplaceThrowableInst(saved_throwable_inst, current_throwable_inst);
453                 skip = true;
454             }
455             if (skip) {
456                 break;
457             }
458         }
459         if (!skip) {
460             new_catch_phi->AppendInput(input_inst);
461             new_catch_phi->AppendThrowableInst(current_throwable_inst);
462         }
463     }
464     if (new_catch_phi->GetInputsCount() == catch_phi->GetInputsCount()) {
465         new_catch_phi->GetBasicBlock()->RemoveInst(new_catch_phi);
466         return nullptr;
467     }
468     return new_catch_phi;
469 }
470 
471 }  // namespace panda::compiler
472