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