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