• 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/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