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
23namespace panda::compiler {
24static 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
34Graph::~Graph()
35{
36}
37
38void 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
70void Graph::AddConstInStartBlock(ConstantInst *const_inst)
71{
72    GetStartBlock()->AppendInst(const_inst);
73}
74
75ParameterInst *Graph::AddNewParameter(uint16_t arg_number)
76{
77    ParameterInst *param = CreateInstParameter(arg_number);
78    GetStartBlock()->AppendInst(param);
79    return param;
80}
81
82void 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
101void InvalidateBlocksOrderAnalyzes(Graph *graph)
102{
103    graph->InvalidateAnalysis<Rpo>();
104    graph->InvalidateAnalysis<DominatorsTree>();
105    graph->InvalidateAnalysis<LinearOrder>();
106}
107
108void 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
117void 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
130const ArenaVector<BasicBlock *> &Graph::GetBlocksRPO() const
131{
132    return GetValidAnalysis<Rpo>().GetBlocks();
133}
134
135const ArenaVector<BasicBlock *> &Graph::GetBlocksLinearOrder() const
136{
137    return GetValidAnalysis<LinearOrder>().GetBlocks();
138}
139
140template <class Callback>
141void Graph::VisitAllInstructions(Callback callback)
142{
143    for (auto bb : GetBlocksRPO()) {
144        for (auto inst : bb->AllInsts()) {
145            callback(inst);
146        }
147    }
148}
149
150BasicBlock *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
159BasicBlock *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
170BasicBlock *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
179BasicBlock *Graph::CreateStartBlock()
180{
181    auto block = CreateEmptyBlock(0U);
182    SetStartBlock(block);
183    return block;
184}
185
186BasicBlock *Graph::CreateEndBlock(uint32_t guest_pc)
187{
188    auto block = CreateEmptyBlock(guest_pc);
189    SetEndBlock(block);
190    return block;
191}
192
193void 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 */
221void 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 */
233void 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
254static 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 */
278void 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
301void 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
324void 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
334void 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 */
345void 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 */
360void 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
373ConstantInst *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
389ConstantInst *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
401void Graph::ResetParameterInfo()
402{
403    param_info_ = nullptr;
404    return;
405}
406
407bool Graph::HasLoop() const
408{
409    ASSERT(GetAnalysis<LoopAnalyzer>().IsValid());
410    return !GetRootLoop()->GetInnerLoops().empty();
411}
412
413bool Graph::HasIrreducibleLoop() const
414{
415    ASSERT(GetAnalysis<LoopAnalyzer>().IsValid());
416    return FlagIrredicibleLoop::Get(bit_fields_);
417}
418
419bool Graph::HasInfiniteLoop() const
420{
421    ASSERT(GetAnalysis<LoopAnalyzer>().IsValid());
422    return FlagInfiniteLoop::Get(bit_fields_);
423}
424
425bool 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 */
434void 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
453std::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)
462Graph::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
472void 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
492void 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
513void 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
528void 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
547int64_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
568uint32_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
580void 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