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