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 "graph.h"
17 #include "basicblock.h"
18 #include "optimizer/analysis/dominators_tree.h"
19 #include "optimizer/analysis/rpo.h"
20 #include "optimizer/analysis/linear_order.h"
21 #include "optimizer/analysis/loop_analyzer.h"
22
23 namespace panda::compiler {
MarkBlocksRec(Marker mrk, BasicBlock *block)24 static void MarkBlocksRec(Marker mrk, BasicBlock *block)
25 {
26 if (block->SetMarker(mrk)) {
27 return;
28 }
29 for (auto succ : block->GetSuccsBlocks()) {
30 MarkBlocksRec(mrk, succ);
31 }
32 }
33
~Graph()34 Graph::~Graph()
35 {
36 }
37
RemoveUnreachableBlocks()38 void Graph::RemoveUnreachableBlocks()
39 {
40 Marker mrk = NewMarker();
41 MarkBlocksRec(mrk, GetStartBlock());
42 // Remove unreachable blocks
43 for (auto &bb : *this) {
44 if (bb == nullptr) {
45 continue;
46 }
47 if (!bb->IsMarked(mrk)) {
48 RemovePredecessors(bb, false);
49 RemoveSuccessors(bb);
50 if (bb->IsTryBegin()) {
51 EraseTryBeginBlock(bb);
52 // Remove try_end mark from paired bb
53 if (!bb->IsEmpty()) {
54 GetTryBeginInst(bb)->GetTryEndBlock()->SetTryEnd(false);
55 }
56 }
57 // Clear DF:
58 for (auto inst : bb->AllInsts()) {
59 inst->RemoveInputs();
60 if (IsInstThrowable(inst)) {
61 RemoveThrowableInst(inst);
62 }
63 }
64 EraseBlock(bb);
65 }
66 }
67 EraseMarker(mrk);
68 }
69
AddConstInStartBlock(ConstantInst *const_inst)70 void Graph::AddConstInStartBlock(ConstantInst *const_inst)
71 {
72 GetStartBlock()->AppendInst(const_inst);
73 }
74
AddNewParameter(uint16_t arg_number)75 ParameterInst *Graph::AddNewParameter(uint16_t arg_number)
76 {
77 ParameterInst *param = CreateInstParameter(arg_number);
78 GetStartBlock()->AppendInst(param);
79 return param;
80 }
81
RemoveConstFromList(ConstantInst *const_inst)82 void Graph::RemoveConstFromList(ConstantInst *const_inst)
83 {
84 if (const_inst == first_const_inst_) {
85 first_const_inst_ = const_inst->GetNextConst();
86 const_inst->SetNextConst(nullptr);
87 return;
88 }
89 auto current = first_const_inst_;
90 auto next = current->GetNextConst();
91 while (next != nullptr && next != const_inst) {
92 current = next;
93 next = next->GetNextConst();
94 }
95 ASSERT(next != nullptr);
96 ASSERT(next == const_inst);
97 current->SetNextConst(const_inst->GetNextConst());
98 const_inst->SetNextConst(nullptr);
99 }
100
InvalidateBlocksOrderAnalyzes(Graph *graph)101 void InvalidateBlocksOrderAnalyzes(Graph *graph)
102 {
103 graph->InvalidateAnalysis<Rpo>();
104 graph->InvalidateAnalysis<DominatorsTree>();
105 graph->InvalidateAnalysis<LinearOrder>();
106 }
107
AddBlock(BasicBlock *block)108 void Graph::AddBlock(BasicBlock *block)
109 {
110 block->SetId(vector_bb_.size());
111 vector_bb_.push_back(block);
112 block->SetGraph(this);
113 InvalidateBlocksOrderAnalyzes(this);
114 }
115
116 #ifndef NDEBUG
AddBlock(BasicBlock *block, uint32_t id)117 void Graph::AddBlock(BasicBlock *block, uint32_t id)
118 {
119 if (vector_bb_.size() <= id) {
120 // (id + 1) for adding a block with index 0
121 vector_bb_.resize((id + 1U) << 1U, nullptr);
122 }
123 ASSERT(vector_bb_[id] == nullptr);
124 block->SetId(id);
125 vector_bb_[id] = block;
126 InvalidateBlocksOrderAnalyzes(this);
127 }
128 #endif
129
GetBlocksRPO() const130 const ArenaVector<BasicBlock *> &Graph::GetBlocksRPO() const
131 {
132 return GetValidAnalysis<Rpo>().GetBlocks();
133 }
134
GetBlocksLinearOrder() const135 const ArenaVector<BasicBlock *> &Graph::GetBlocksLinearOrder() const
136 {
137 return GetValidAnalysis<LinearOrder>().GetBlocks();
138 }
139
140 template <class Callback>
VisitAllInstructions(Callback callback)141 void Graph::VisitAllInstructions(Callback callback)
142 {
143 for (auto bb : GetBlocksRPO()) {
144 for (auto inst : bb->AllInsts()) {
145 callback(inst);
146 }
147 }
148 }
149
CreateEmptyBlock(uint32_t guest_pc)150 BasicBlock *Graph::CreateEmptyBlock(uint32_t guest_pc)
151 {
152 auto block = GetAllocator()->New<BasicBlock>(this, guest_pc);
153 CHECK_NOT_NULL(block);
154 AddBlock(block);
155 return block;
156 }
157
158 // Create empty block with base block's properties
CreateEmptyBlock(BasicBlock *base_block)159 BasicBlock *Graph::CreateEmptyBlock(BasicBlock *base_block)
160 {
161 ASSERT(base_block != nullptr);
162 auto block = CreateEmptyBlock();
163 block->SetGuestPc(base_block->GetGuestPc());
164 block->SetAllFields(base_block->GetAllFields());
165 block->SetTryId(base_block->GetTryId());
166 return block;
167 }
168
169 #ifndef NDEBUG
CreateEmptyBlock(uint32_t id, uint32_t guest_pc)170 BasicBlock *Graph::CreateEmptyBlock(uint32_t id, uint32_t guest_pc)
171 {
172 auto block = GetAllocator()->New<BasicBlock>(this, guest_pc);
173 CHECK_NOT_NULL(block);
174 AddBlock(block, id);
175 return block;
176 }
177 #endif
178
CreateStartBlock()179 BasicBlock *Graph::CreateStartBlock()
180 {
181 auto block = CreateEmptyBlock(0U);
182 SetStartBlock(block);
183 return block;
184 }
185
CreateEndBlock(uint32_t guest_pc)186 BasicBlock *Graph::CreateEndBlock(uint32_t guest_pc)
187 {
188 auto block = CreateEmptyBlock(guest_pc);
189 SetEndBlock(block);
190 return block;
191 }
192
RemovePredecessorUpdateDF(BasicBlock *block, BasicBlock *rm_pred)193 void RemovePredecessorUpdateDF(BasicBlock *block, BasicBlock *rm_pred)
194 {
195 constexpr auto IMM_2 = 2;
196 if (block->GetPredsBlocks().size() == IMM_2) {
197 for (auto phi : block->PhiInstsSafe()) {
198 auto rm_index = phi->CastToPhi()->GetPredBlockIndex(rm_pred);
199 auto remaining_inst = phi->GetInput(1 - rm_index).GetInst();
200 if (phi != remaining_inst && remaining_inst->GetBasicBlock() != nullptr) {
201 phi->ReplaceUsers(remaining_inst);
202 }
203 block->RemoveInst(phi);
204 }
205 } else if (block->GetPredsBlocks().size() > IMM_2) {
206 for (auto phi : block->PhiInstsSafe()) {
207 auto rm_index = phi->CastToPhi()->GetPredBlockIndex(rm_pred);
208 phi->CastToPhi()->RemoveInput(rm_index);
209 }
210 } else {
211 ASSERT(block->GetPredsBlocks().size() == 1);
212 }
213 block->RemovePred(rm_pred);
214 InvalidateBlocksOrderAnalyzes(block->GetGraph());
215 }
216
217 /*
218 * Remove edges between `block` and its successors and
219 * update phi-instructions in successors blocks
220 */
RemoveSuccessors(BasicBlock *block)221 void Graph::RemoveSuccessors(BasicBlock *block)
222 {
223 for (auto succ : block->GetSuccsBlocks()) {
224 RemovePredecessorUpdateDF(succ, block);
225 }
226 block->GetSuccsBlocks().clear();
227 }
228
229 /*
230 * Remove edges between `block` and its predecessors,
231 * update last instructions in predecessors blocks
232 */
RemovePredecessors(BasicBlock *block, bool remove_last_inst)233 void Graph::RemovePredecessors(BasicBlock *block, bool remove_last_inst)
234 {
235 for (auto pred : block->GetPredsBlocks()) {
236 if (remove_last_inst && !pred->IsTryBegin() && !pred->IsTryEnd()) {
237 if (pred->GetSuccsBlocks().size() == 2U) {
238 auto last = pred->GetLastInst();
239 ASSERT(last->GetOpcode() == Opcode::If || last->GetOpcode() == Opcode::IfImm);
240 pred->RemoveInst(last);
241 } else {
242 ASSERT(pred->GetSuccsBlocks().size() == 1 && pred->GetSuccessor(0) == block);
243 }
244 }
245 if (std::find(pred->GetSuccsBlocks().begin(), pred->GetSuccsBlocks().end(), block) !=
246 pred->GetSuccsBlocks().end()) {
247 pred->RemoveSucc(block);
248 }
249 }
250 block->GetPredsBlocks().clear();
251 }
252
253 // Helper for the next 2 methods
FinishBlockRemoval(BasicBlock *block)254 static void FinishBlockRemoval(BasicBlock *block)
255 {
256 auto graph = block->GetGraph();
257 graph->GetAnalysis<DominatorsTree>().SetValid(true);
258 auto dominator = block->GetDominator();
259 if (dominator != nullptr) {
260 dominator->RemoveDominatedBlock(block);
261 for (auto dom_block : block->GetDominatedBlocks()) {
262 ASSERT(dom_block->GetDominator() == block);
263 dominator->AddDominatedBlock(dom_block);
264 dom_block->SetDominator(dominator);
265 }
266 }
267 block->SetDominator(nullptr);
268
269 block->SetGraph(nullptr);
270 if (graph->GetAnalysis<Rpo>().IsValid()) {
271 graph->GetAnalysis<Rpo>().RemoveBasicBlock(block);
272 }
273 }
274
275 /**
276 * @param block - a block which is disconnecting from the graph with clearing control-flow and data-flow
277 */
DisconnectBlock(BasicBlock *block, bool remove_last_inst, bool fix_dom_tree)278 void Graph::DisconnectBlock(BasicBlock *block, bool remove_last_inst, bool fix_dom_tree)
279 {
280 ASSERT(IsAnalysisValid<DominatorsTree>() || !fix_dom_tree);
281 RemovePredecessors(block, remove_last_inst);
282 RemoveSuccessors(block);
283
284 if (block->IsTryBegin()) {
285 EraseTryBeginBlock(block);
286 }
287
288 // Remove all instructions from `block`
289 block->Clear();
290
291 if (block->IsEndBlock()) {
292 SetEndBlock(nullptr);
293 }
294 if (fix_dom_tree) {
295 FinishBlockRemoval(block);
296 }
297 EraseBlock(block);
298 // NB! please do not forget to fix LoopAnalyzer or invalidate it after the end of the pass
299 }
300
DisconnectBlockRec(BasicBlock *block, bool remove_last_inst, bool fix_dom_tree)301 void Graph::DisconnectBlockRec(BasicBlock *block, bool remove_last_inst, bool fix_dom_tree)
302 {
303 if (block->GetGraph() == nullptr) {
304 return;
305 }
306 bool loop_flag = false;
307 if (block->IsLoopHeader()) {
308 loop_flag = true;
309 auto loop = block->GetLoop();
310 for (auto pred : block->GetPredsBlocks()) {
311 loop_flag &= (std::find(loop->GetBackEdges().begin(), loop->GetBackEdges().end(), pred) !=
312 loop->GetBackEdges().end());
313 }
314 }
315 if (block->GetPredsBlocks().empty() || loop_flag) {
316 ArenaVector<BasicBlock *> succs(block->GetSuccsBlocks(), GetLocalAllocator()->Adapter());
317 DisconnectBlock(block, remove_last_inst, fix_dom_tree);
318 for (auto succ : succs) {
319 DisconnectBlockRec(succ, remove_last_inst, fix_dom_tree);
320 }
321 }
322 }
323
EraseBlock(BasicBlock *block)324 void Graph::EraseBlock(BasicBlock *block)
325 {
326 vector_bb_[block->GetId()] = nullptr;
327 if (GetEndBlock() == block) {
328 SetEndBlock(nullptr);
329 }
330 ASSERT(GetStartBlock() != block);
331 block->SetGraph(nullptr);
332 }
333
RestoreBlock(BasicBlock *block)334 void Graph::RestoreBlock(BasicBlock *block)
335 {
336 ASSERT(vector_bb_[block->GetId()] == nullptr);
337 vector_bb_[block->GetId()] = block;
338 block->SetGraph(this);
339 InvalidateBlocksOrderAnalyzes(this);
340 }
341
342 /**
343 * @param block - same for block without instructions at all
344 */
RemoveEmptyBlock(BasicBlock *block)345 void Graph::RemoveEmptyBlock(BasicBlock *block)
346 {
347 ASSERT(IsAnalysisValid<DominatorsTree>());
348 ASSERT(block->GetLastInst() == nullptr);
349 ASSERT(block->GetPredsBlocks().empty());
350 ASSERT(block->GetSuccsBlocks().empty());
351
352 FinishBlockRemoval(block);
353 EraseBlock(block);
354 // NB! please do not forget to fix LoopAnalyzer or invalidate it after the end of the pass
355 }
356
357 /**
358 * @param block - same for block without instructions, may have Phi(s)
359 */
RemoveEmptyBlockWithPhis(BasicBlock *block, bool irr_loop)360 void Graph::RemoveEmptyBlockWithPhis(BasicBlock *block, bool irr_loop)
361 {
362 ASSERT(IsAnalysisValid<DominatorsTree>());
363 ASSERT(block->IsEmpty());
364
365 ASSERT(!block->GetSuccsBlocks().empty());
366 ASSERT(!block->GetPredsBlocks().empty());
367 block->RemoveEmptyBlock(irr_loop);
368
369 FinishBlockRemoval(block);
370 EraseBlock(block);
371 }
372
FindConstant(DataType::Type type, uint64_t value)373 ConstantInst *Graph::FindConstant(DataType::Type type, uint64_t value)
374 {
375 for (auto constant = GetFirstConstInst(); constant != nullptr; constant = constant->GetNextConst()) {
376 if (constant->GetType() != type) {
377 continue;
378 }
379 if (IsBytecodeOptimizer() && IsInt32Bit(type) && (constant->GetInt32Value() == static_cast<uint32_t>(value))) {
380 return constant;
381 }
382 if (constant->IsEqualConst(type, value)) {
383 return constant;
384 }
385 }
386 return nullptr;
387 }
388
FindOrAddConstant(ConstantInst *inst)389 ConstantInst *Graph::FindOrAddConstant(ConstantInst *inst)
390 {
391 auto existing_const = FindConstant(inst->GetType(), inst->GetRawValue());
392 if (existing_const != nullptr) {
393 return existing_const;
394 }
395 AddConstInStartBlock(inst);
396 inst->SetNextConst(first_const_inst_);
397 first_const_inst_ = inst;
398 return inst;
399 }
400
ResetParameterInfo()401 void Graph::ResetParameterInfo()
402 {
403 param_info_ = nullptr;
404 return;
405 }
406
HasLoop() const407 bool Graph::HasLoop() const
408 {
409 ASSERT(GetAnalysis<LoopAnalyzer>().IsValid());
410 return !GetRootLoop()->GetInnerLoops().empty();
411 }
412
HasIrreducibleLoop() const413 bool Graph::HasIrreducibleLoop() const
414 {
415 ASSERT(GetAnalysis<LoopAnalyzer>().IsValid());
416 return FlagIrredicibleLoop::Get(bit_fields_);
417 }
418
HasInfiniteLoop() const419 bool Graph::HasInfiniteLoop() const
420 {
421 ASSERT(GetAnalysis<LoopAnalyzer>().IsValid());
422 return FlagInfiniteLoop::Get(bit_fields_);
423 }
424
HasFloatRegs() const425 bool Graph::HasFloatRegs() const
426 {
427 ASSERT(IsRegAllocApplied());
428 return FlagFloatRegs::Get(bit_fields_);
429 }
430
431 /*
432 * Mark blocks, which have successor from external loop
433 */
MarkLoopExits(const Graph *graph, Marker marker)434 void MarkLoopExits(const Graph *graph, Marker marker)
435 {
436 for (auto block : graph->GetBlocksRPO()) {
437 if (block->GetSuccsBlocks().size() == MAX_SUCCS_NUM) {
438 if (block->GetSuccessor(0)->GetLoop() != block->GetSuccessor(1)->GetLoop()) {
439 block->SetMarker(marker);
440 }
441 } else if (block->GetSuccsBlocks().size() > MAX_SUCCS_NUM) {
442 ASSERT(block->IsTryEnd() || block->IsTryBegin());
443 auto loop = block->GetSuccessor(0)->GetLoop();
444 for (size_t i = 1; i < block->GetSuccsBlocks().size(); i++) {
445 if (loop != block->GetSuccessor(i)->GetLoop()) {
446 block->SetMarker(marker);
447 }
448 }
449 }
450 }
451 }
452
GetMethodFullName(const Graph *graph, RuntimeInterface::MethodPtr method)453 std::string GetMethodFullName(const Graph *graph, RuntimeInterface::MethodPtr method)
454 {
455 std::stringstream sstream;
456 sstream << graph->GetRuntime()->GetClassNameFromMethod(method)
457 << "::" << graph->GetRuntime()->GetMethodName(method);
458 return sstream.str();
459 }
460
461 // NOLINTNEXTLINE(readability-identifier-naming,-warnings-as-errors)
begin()462 Graph::ParameterList::Iterator Graph::ParameterList::begin()
463 {
464 auto start_bb = graph_->GetStartBlock();
465 Iterator it(start_bb->GetFirstInst());
466 if (*it != nullptr && it->GetOpcode() != Opcode::Parameter) {
467 ++it;
468 }
469 return it;
470 }
471
RemoveThrowableInst(const Inst *inst)472 void Graph::RemoveThrowableInst(const Inst *inst)
473 {
474 ASSERT(IsInstThrowable(inst));
475 for (auto catch_handler : throwable_insts_.at(inst)) {
476 for (auto catch_inst : catch_handler->AllInsts()) {
477 if (!catch_inst->IsCatchPhi() || catch_inst->CastToCatchPhi()->IsAcc()) {
478 continue;
479 }
480 auto catch_phi = catch_inst->CastToCatchPhi();
481 const auto &vregs = catch_phi->GetThrowableInsts();
482 auto it = std::find(vregs->begin(), vregs->end(), inst);
483 if (it != vregs->end()) {
484 int index = std::distance(vregs->begin(), it);
485 catch_phi->RemoveInput(index);
486 }
487 }
488 }
489 throwable_insts_.erase(inst);
490 }
491
ReplaceThrowableInst(Inst *old_inst, Inst *new_inst)492 void Graph::ReplaceThrowableInst(Inst *old_inst, Inst *new_inst)
493 {
494 auto it = throwable_insts_.emplace(new_inst, GetAllocator()->Adapter()).first;
495 it->second = std::move(throwable_insts_.at(old_inst));
496
497 for (auto catch_handler : it->second) {
498 for (auto catch_inst : catch_handler->AllInsts()) {
499 if (!catch_inst->IsCatchPhi() || catch_inst->CastToCatchPhi()->IsAcc()) {
500 continue;
501 }
502 auto catch_phi = catch_inst->CastToCatchPhi();
503 const auto &vregs = catch_phi->GetThrowableInsts();
504 auto iter = std::find(vregs->begin(), vregs->end(), old_inst);
505 if (iter != vregs->end()) {
506 catch_phi->ReplaceThrowableInst(old_inst, new_inst);
507 }
508 }
509 }
510 throwable_insts_.erase(old_inst);
511 }
512
DumpThrowableInsts(std::ostream *out) const513 void Graph::DumpThrowableInsts(std::ostream *out) const
514 {
515 for (auto &[inst, handlers] : throwable_insts_) {
516 (*out) << "Throwable Inst";
517 inst->Dump(out);
518 (*out) << "Catch handlers:";
519 auto sep = " ";
520 for (auto bb : handlers) {
521 (*out) << sep << "BB " << bb->GetId();
522 sep = ", ";
523 }
524 (*out) << std::endl;
525 }
526 }
527
InitDefaultLocations()528 void Graph::InitDefaultLocations()
529 {
530 if (IsDefaultLocationsInit()) {
531 return;
532 }
533 VisitAllInstructions([this](Inst *inst) {
534 if (!inst->IsOperandsDynamic() || inst->IsPhi()) {
535 return;
536 }
537 [[maybe_unused]] LocationsInfo *locations = GetAllocator()->New<LocationsInfo>(GetAllocator(), inst);
538 for (size_t i = 0; i < inst->GetInputsCount(); i++) {
539 if (inst->GetInputType(i) != DataType::NO_TYPE) {
540 locations->SetLocation(i, Location::RequireRegister());
541 }
542 }
543 });
544 SetDefaultLocationsInit();
545 }
546
GetBranchCounter(const BasicBlock *block, bool true_succ)547 int64_t Graph::GetBranchCounter(const BasicBlock *block, bool true_succ)
548 {
549 ASSERT(block->GetSuccsBlocks().size() == MAX_SUCCS_NUM);
550 auto last_inst = block->GetLastInst();
551 RuntimeInterface::MethodPtr method;
552 if (last_inst->GetOpcode() == Opcode::IfImm) {
553 method = last_inst->CastToIfImm()->GetMethod();
554 } else if (last_inst->GetOpcode() == Opcode::If) {
555 method = last_inst->CastToIf()->GetMethod();
556 } else {
557 return 0;
558 }
559
560 if (method == nullptr) {
561 // corresponded branch instruction was not present in bytecode, e.g. IfImmInst was created while inlining
562 return 0;
563 }
564
565 return block->IsInverted() == 0;
566 }
567
GetParametersSlotsCount() const568 uint32_t Graph::GetParametersSlotsCount() const
569 {
570 uint32_t max_slot = 0;
571 for (auto param_inst : GetParameters()) {
572 auto location = param_inst->CastToParameter()->GetLocationData().GetSrc();
573 if (location.IsStackParameter()) {
574 max_slot = location.GetValue() + 1U;
575 }
576 }
577 return max_slot;
578 }
579
Dump(std::ostream &stm)580 void GraphMode::Dump(std::ostream &stm)
581 {
582 const char *sep = "";
583 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
584 #define DUMP_MODE(name) \
585 if (Is##name()) { \
586 stm << sep << #name; \
587 sep = ", "; \
588 }
589
590 DUMP_MODE(Osr);
591 DUMP_MODE(BytecodeOpt);
592 DUMP_MODE(DynamicMethod);
593 DUMP_MODE(Native);
594 DUMP_MODE(FastPath);
595 DUMP_MODE(Boundary);
596 DUMP_MODE(Interpreter);
597 DUMP_MODE(InterpreterEntry);
598 }
599
600 } // namespace panda::compiler
601