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#ifndef COMPILER_OPTIMIZER_IR_GRAPH_H
17#define COMPILER_OPTIMIZER_IR_GRAPH_H
18
19#include <algorithm>
20#include <optional>
21#include "compiler_events_gen.h"
22#include "inst.h"
23#include "marker.h"
24#include "optimizer/pass_manager.h"
25#include "utils/arena_containers.h"
26
27namespace panda {
28class Method;
29class CodeAllocator;
30}  // namespace panda
31
32namespace panda::compiler {
33class BasicBlock;
34class Graph;
35class RuntimeInfo;
36class PassManager;
37class LivenessAnalyzer;
38class DominatorsTree;
39class Rpo;
40class Loop;
41class ParameterInfo;
42
43/**
44 * Specifies graph compilation mode.
45 */
46class GraphMode {
47public:
48    explicit GraphMode(uint32_t value) : value_(value) {}
49
50// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
51#define DECLARE_GRAPH_MODE(name)                    \
52    static GraphMode name(bool set = true)          \
53    {                                               \
54        return GraphMode(Flag##name ::Encode(set)); \
55    }                                               \
56    void Set##name(bool v)                          \
57    {                                               \
58        Flag##name ::Set(v, &value_);               \
59    }                                               \
60    bool Is##name() const                           \
61    {                                               \
62        return Flag##name ::Get(value_);            \
63    }
64
65    DECLARE_GRAPH_MODE(Osr);
66    // The graph is used in BytecodeOptimizer mode
67    DECLARE_GRAPH_MODE(BytecodeOpt);
68    // The method from dynamic language
69    DECLARE_GRAPH_MODE(DynamicMethod);
70    // Graph will be compiled with native calling convention
71    DECLARE_GRAPH_MODE(Native);
72    // FastPath from compiled code to runtime
73    DECLARE_GRAPH_MODE(FastPath);
74    // Boundary frame is used for compiled code
75    DECLARE_GRAPH_MODE(Boundary);
76    // Graph will be compiled for calling inside interpreter
77    DECLARE_GRAPH_MODE(Interpreter);
78    // Graph will be compiled for interpreter main loop
79    DECLARE_GRAPH_MODE(InterpreterEntry);
80
81#undef DECLARE_GRAPH_MODE
82
83    bool SupportManagedCode() const
84    {
85        return !IsNative() && !IsFastPath() && !IsBoundary() && !IsInterpreter() && !IsInterpreterEntry();
86    }
87
88    void Dump(std::ostream &stm);
89
90private:
91    using FlagOsr = BitField<bool, 0, 1>;
92    using FlagBytecodeOpt = FlagOsr::NextFlag;
93    using FlagDynamicMethod = FlagBytecodeOpt::NextFlag;
94    using FlagNative = FlagDynamicMethod::NextFlag;
95    using FlagFastPath = FlagNative::NextFlag;
96    using FlagBoundary = FlagFastPath::NextFlag;
97    using FlagInterpreter = FlagBoundary::NextFlag;
98    using FlagInterpreterEntry = FlagInterpreter::NextFlag;
99
100    uint32_t value_ {0};
101
102    friend GraphMode operator|(GraphMode a, GraphMode b);
103};
104
105inline GraphMode operator|(GraphMode a, GraphMode b)
106{
107    return GraphMode(a.value_ | b.value_);
108}
109
110using EncodeDataType = Span<uint8_t>;
111
112class Graph final : public MarkerMgr {
113public:
114    explicit Graph(ArenaAllocator *allocator, ArenaAllocator *local_allocator, Arch arch)
115        : Graph(allocator, local_allocator, arch, false)
116    {
117    }
118
119    Graph(ArenaAllocator *allocator, ArenaAllocator *local_allocator, Arch arch, bool osr_mode)
120        : Graph(allocator, local_allocator, arch, nullptr, GetDefaultRuntime(), osr_mode)
121    {
122    }
123
124    Graph(ArenaAllocator *allocator, ArenaAllocator *local_allocator, Arch arch, bool dynamic_method, bool bytecode_opt)
125        : Graph(allocator, local_allocator, arch, nullptr, GetDefaultRuntime(), false, nullptr, dynamic_method,
126                bytecode_opt)
127    {
128    }
129
130    Graph(ArenaAllocator *allocator, ArenaAllocator *local_allocator, Arch arch, RuntimeInterface::MethodPtr method,
131          RuntimeInterface *runtime, bool osr_mode)
132        : Graph(allocator, local_allocator, arch, method, runtime, osr_mode, nullptr)
133    {
134    }
135
136    Graph(ArenaAllocator *allocator, ArenaAllocator *local_allocator, Arch arch, RuntimeInterface::MethodPtr method,
137          RuntimeInterface *runtime, bool osr_mode, Graph *parent, bool dynamic_method = false,
138          bool bytecode_opt = false)
139        : Graph(allocator, local_allocator, arch, method, runtime, parent,
140                GraphMode::Osr(osr_mode) | GraphMode::BytecodeOpt(bytecode_opt) |
141                    GraphMode::DynamicMethod(dynamic_method))
142    {
143    }
144
145    Graph(ArenaAllocator *allocator, ArenaAllocator *local_allocator, Arch arch, RuntimeInterface::MethodPtr method,
146          RuntimeInterface *runtime, Graph *parent, GraphMode mode)
147        : ALLOCATOR(allocator),
148          LOCAL_ALLOCATOR(local_allocator),
149          arch_(arch),
150          vector_bb_(allocator->Adapter()),
151          throwable_insts_(allocator->Adapter()),
152          runtime_(runtime),
153          method_(method),
154          pass_manager_(this, parent != nullptr ? parent->GetPassManager() : nullptr),
155          event_writer_(runtime->GetClassNameFromMethod(method), runtime->GetMethodName(method)),
156          mode_(mode),
157          single_implementation_list_(allocator->Adapter()),
158          try_begin_blocks_(allocator->Adapter()),
159          spilled_constants_(allocator->Adapter()),
160          parent_graph_(parent)
161    {
162        SetNeedCleanup(true);
163    }
164
165    ~Graph() override;
166
167    Graph *CreateChildGraph(RuntimeInterface::MethodPtr method)
168    {
169        auto graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), GetArch(), method, GetRuntime(),
170                                                this, mode_);
171        return graph;
172    }
173
174    /// Get default runtime interface object
175    static RuntimeInterface *GetDefaultRuntime()
176    {
177        static RuntimeInterface runtime_interface;
178        return &runtime_interface;
179    }
180
181    Arch GetArch() const
182    {
183        return arch_;
184    }
185
186    void AddBlock(BasicBlock *block);
187#ifndef NDEBUG
188    void AddBlock(BasicBlock *block, uint32_t id);
189#endif
190    void DisconnectBlock(BasicBlock *block, bool remove_last_inst = true, bool fix_dom_tree = true);
191    void DisconnectBlockRec(BasicBlock *block, bool remove_last_inst = true, bool fix_dom_tree = true);
192
193    void EraseBlock(BasicBlock *block);
194    void RestoreBlock(BasicBlock *block);
195    // Remove empty block. Block must have one successor and no Phis.
196    void RemoveEmptyBlock(BasicBlock *block);
197
198    // Remove empty block. Block may have Phis and can't be a loop pre-header.
199    void RemoveEmptyBlockWithPhis(BasicBlock *block, bool irr_loop = false);
200
201    // Remove block predecessors.
202    void RemovePredecessors(BasicBlock *block, bool remove_last_inst = true);
203
204    // Remove block successors.
205    void RemoveSuccessors(BasicBlock *block);
206
207    // Remove unreachable blocks.
208    void RemoveUnreachableBlocks();
209
210    // get end block
211    BasicBlock *GetEndBlock()
212    {
213        return end_block_;
214    }
215
216    BasicBlock *GetEndBlock() const
217    {
218        return end_block_;
219    }
220    // set end block
221    void SetEndBlock(BasicBlock *end_block)
222    {
223        end_block_ = end_block;
224    }
225    bool HasEndBlock()
226    {
227        return end_block_ != nullptr;
228    }
229    // get start block
230    BasicBlock *GetStartBlock()
231    {
232        return start_block_;
233    }
234    BasicBlock *GetStartBlock() const
235    {
236        return start_block_;
237    }
238    // set start block
239    void SetStartBlock(BasicBlock *start_block)
240    {
241        start_block_ = start_block;
242    }
243    // get vector_bb_
244    const ArenaVector<BasicBlock *> &GetVectorBlocks() const
245    {
246        return vector_bb_;
247    }
248
249    size_t GetAliveBlocksCount() const
250    {
251        return std::count_if(vector_bb_.begin(), vector_bb_.end(), [](BasicBlock *block) { return block != nullptr; });
252    }
253
254    PassManager *GetPassManager()
255    {
256        return &pass_manager_;
257    }
258    const PassManager *GetPassManager() const
259    {
260        return &pass_manager_;
261    }
262
263    const ArenaVector<BasicBlock *> &GetBlocksRPO() const;
264
265    const ArenaVector<BasicBlock *> &GetBlocksLinearOrder() const;
266
267    template <class Callback>
268    void VisitAllInstructions(Callback callback);
269
270    /// Main allocator for graph, all related to Graph data should be allocated via this allocator.
271    ArenaAllocator *GetAllocator() const
272    {
273        return ALLOCATOR;
274    }
275    /// Allocator for temproray usage, when allocated data is no longer needed after optimization/analysis finished.
276    ArenaAllocator *GetLocalAllocator() const
277    {
278        return LOCAL_ALLOCATOR;
279    }
280    bool IsDFConstruct() const
281    {
282        return FlagDFConstruct::Get(bit_fields_);
283    }
284    void SetDFConstruct()
285    {
286        FlagDFConstruct::Set(true, &bit_fields_);
287    }
288
289    bool IsAotMode() const
290    {
291        return false;
292    }
293
294    bool IsOfflineCompilationMode() const
295    {
296        return IsAotMode() || GetMode().IsInterpreter();
297    }
298
299    bool IsDefaultLocationsInit() const
300    {
301        return FlagDefaultLocationsInit::Get(bit_fields_);
302    }
303    void SetDefaultLocationsInit()
304    {
305        FlagDefaultLocationsInit::Set(true, &bit_fields_);
306    }
307#ifndef NDEBUG
308    bool IsRegAllocApplied() const
309    {
310        return FlagRegallocApplied::Get(bit_fields_);
311    }
312    void SetRegAllocApplied()
313    {
314        FlagRegallocApplied::Set(true, &bit_fields_);
315    }
316    bool IsRegAccAllocApplied() const
317    {
318        return FlagRegaccallocApplied::Get(bit_fields_);
319    }
320    void SetRegAccAllocApplied()
321    {
322        FlagRegaccallocApplied::Set(true, &bit_fields_);
323    }
324    bool IsInliningComplete() const
325    {
326        return FlagInliningComplete::Get(bit_fields_);
327    }
328    void SetInliningComplete()
329    {
330        FlagInliningComplete::Set(true, &bit_fields_);
331    }
332    bool IsSchedulerComplete() const
333    {
334        return FlagSchedulerComplete::Get(bit_fields_);
335    }
336    void SetSchedulerComplete()
337    {
338        FlagSchedulerComplete::Set(true, &bit_fields_);
339    }
340    bool IsLowLevelInstructionsEnabled() const
341    {
342        return FlagLowLevelInstnsEnabled::Get(bit_fields_);
343    }
344    void SetLowLevelInstructionsEnabled()
345    {
346        FlagLowLevelInstnsEnabled::Set(true, &bit_fields_);
347    }
348#else
349    bool IsRegAllocApplied() const
350    {
351        return false;
352    }
353#endif  // NDEBUG
354
355    void SetData(EncodeDataType data)
356    {
357        data_ = data;
358    }
359
360    EncodeDataType GetData() const
361    {
362        return data_;
363    }
364
365    EncodeDataType GetData()
366    {
367        return data_;
368    }
369
370    void SetCodeInfo(Span<uint8_t> data)
371    {
372        code_info_data_ = data.SubSpan<const uint8_t>(0, data.size());
373    }
374
375    Span<const uint8_t> GetCodeInfoData() const
376    {
377        return code_info_data_;
378    }
379
380    void DumpUsedRegs(std::ostream &out = std::cerr, const char *prefix = nullptr) const
381    {
382        if (prefix != nullptr) {
383            out << prefix;
384        }
385        out << "'\n  used scalar regs: ";
386        if (used_regs_ != nullptr) {
387            for (unsigned i = 0; i < used_regs_->size(); ++i) {
388                if (used_regs_->at(i)) {
389                    out << i << " ";
390                }
391            }
392        }
393        out << "\n  used float  regs: ";
394        if (used_regs_ != nullptr) {
395            for (unsigned i = 0; i < used_vregs_->size(); ++i) {
396                if (used_vregs_->at(i)) {
397                    out << i << " ";
398                }
399            }
400        }
401        out << std::endl;
402    }
403
404    // Get registers mask which used in graph
405    template <DataType::Type reg_type>
406    ArenaVector<bool> *GetUsedRegs() const
407    {
408        // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
409        if constexpr (reg_type == DataType::INT64) {
410            return used_regs_;
411        }
412        // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
413        if constexpr (reg_type == DataType::FLOAT64) {
414            return used_vregs_;
415        }
416        UNREACHABLE();
417        return nullptr;
418    }
419
420    void SetRegUsage(Register reg, DataType::Type type)
421    {
422        ASSERT(reg != INVALID_REG);
423        if (DataType::IsFloatType(type)) {
424            SetUsedReg<DataType::FLOAT64>(reg);
425        } else {
426            SetUsedReg<DataType::INT64>(reg);
427        }
428    }
429
430    template <DataType::Type reg_type>
431    void SetUsedReg(Register reg)
432    {
433        ArenaVector<bool> *graph_regs = nullptr;
434        // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
435        if constexpr (reg_type == DataType::INT64) {
436            graph_regs = used_regs_;
437            // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
438        } else if constexpr (reg_type == DataType::FLOAT64) {
439            graph_regs = used_vregs_;
440        } else {
441            UNREACHABLE();
442        }
443        CHECK_NOT_NULL(graph_regs);
444        ASSERT(reg < graph_regs->size());
445        (*graph_regs)[reg] = true;
446    }
447
448    template <DataType::Type reg_type>
449    void InitUsedRegs(const ArenaVector<bool> *used_regs)
450    {
451        if (used_regs == nullptr) {
452            return;
453        }
454        ArenaVector<bool> *graph_regs = nullptr;
455        // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
456        if constexpr (reg_type == DataType::INT64) {
457            used_regs_ = GetAllocator()->New<ArenaVector<bool>>(GetAllocator()->Adapter());
458            graph_regs = used_regs_;
459            // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
460        } else if constexpr (reg_type == DataType::FLOAT64) {
461            used_vregs_ = GetAllocator()->New<ArenaVector<bool>>(GetAllocator()->Adapter());
462            graph_regs = used_vregs_;
463        } else {
464            UNREACHABLE();
465        }
466        CHECK_NOT_NULL(graph_regs);
467        graph_regs->resize(used_regs->size());
468        std::copy(used_regs->begin(), used_regs->end(), graph_regs->begin());
469    }
470
471    uint32_t GetStackSlotsCount() const
472    {
473        return stack_slot_count_;
474    }
475
476    void SetStackSlotsCount(uint32_t stack_slot_count)
477    {
478        stack_slot_count_ = stack_slot_count;
479    }
480
481    void UpdateStackSlotsCount(uint32_t stack_slot_count)
482    {
483        stack_slot_count_ = std::max(stack_slot_count_, stack_slot_count);
484    }
485
486    uint32_t GetParametersSlotsCount() const;
487
488    uint32_t GetExtSlotsStart() const
489    {
490        return ext_stack_slot_;
491    }
492
493    void SetExtSlotsStart(uint32_t ext_stack_slot)
494    {
495        ext_stack_slot_ = ext_stack_slot;
496    }
497
498    BasicBlock *CreateEmptyBlock(uint32_t guest_pc = INVALID_PC);
499    BasicBlock *CreateEmptyBlock(BasicBlock *base_block);
500#ifndef NDEBUG
501    BasicBlock *CreateEmptyBlock(uint32_t id, uint32_t guest_pc);
502#endif
503    BasicBlock *CreateStartBlock();
504    BasicBlock *CreateEndBlock(uint32_t guest_pc = INVALID_PC);
505    ConstantInst *GetFirstConstInst()
506    {
507        return first_const_inst_;
508    }
509    void SetFirstConstInst(ConstantInst *const_inst)
510    {
511        first_const_inst_ = const_inst;
512    }
513
514    Inst *GetNullPtrInst() const
515    {
516        return nullptr_inst_;
517    }
518    bool HasNullPtrInst() const
519    {
520        return nullptr_inst_ != nullptr;
521    }
522    void UnsetNullPtrInst()
523    {
524        ASSERT(HasNullPtrInst());
525        nullptr_inst_ = nullptr;
526    }
527
528    /// Find constant in the list, return nullptr if not found
529    ConstantInst *FindConstant(DataType::Type type, uint64_t value);
530    /// Find constant in the list or create new one and insert at the end
531    template <typename T>
532    ConstantInst *FindOrCreateConstant(T value);
533
534    /**
535     * Find constant that is equal to the given one specified by inst. If not found, add inst to the graph.
536     * @param inst Constant instruction to be added
537     * @return Found instruction or inst if not found
538     */
539    ConstantInst *FindOrAddConstant(ConstantInst *inst);
540
541    ParameterInst *AddNewParameter(uint16_t arg_number);
542
543    ParameterInst *AddNewParameter(uint16_t arg_number, DataType::Type type)
544    {
545        ParameterInst *param = AddNewParameter(arg_number);
546        param->SetType(type);
547        return param;
548    }
549
550    /*
551     * The function remove the ConstantInst from the graph list
552     * !NOTE ConstantInst isn't removed from BasicBlock list
553     */
554    void RemoveConstFromList(ConstantInst *const_inst);
555
556    ConstantInst *GetSpilledConstant(ImmTableSlot slot)
557    {
558        ASSERT(static_cast<size_t>(slot) < spilled_constants_.size());
559        return spilled_constants_[slot];
560    }
561
562    ImmTableSlot AddSpilledConstant(ConstantInst *const_inst)
563    {
564        // Constant already in the table
565        auto current_slot = const_inst->GetImmTableSlot();
566        if (current_slot != INVALID_IMM_TABLE_SLOT) {
567            ASSERT(spilled_constants_[current_slot] == const_inst);
568            return current_slot;
569        }
570
571        auto count = spilled_constants_.size();
572        if (count >= MAX_NUM_IMM_SLOTS) {
573            return INVALID_IMM_TABLE_SLOT;
574        }
575        spilled_constants_.push_back(const_inst);
576        const_inst->SetImmTableSlot(count);
577        return ImmTableSlot(count);
578    }
579
580    ImmTableSlot FindSpilledConstantSlot(ConstantInst *const_inst) const
581    {
582        auto slot = std::find(spilled_constants_.begin(), spilled_constants_.end(), const_inst);
583        if (slot == spilled_constants_.end()) {
584            return INVALID_IMM_TABLE_SLOT;
585        }
586        return std::distance(spilled_constants_.begin(), slot);
587    }
588
589    size_t GetSpilledConstantsCount() const
590    {
591        return spilled_constants_.size();
592    }
593
594    bool HasAvailableConstantSpillSlots() const
595    {
596        return GetSpilledConstantsCount() < MAX_NUM_IMM_SLOTS;
597    }
598
599    auto begin()  // NOLINT(readability-identifier-naming)
600    {
601        return vector_bb_.begin();
602    }
603    auto begin() const  // NOLINT(readability-identifier-naming)
604    {
605        return vector_bb_.begin();
606    }
607    auto end()  // NOLINT(readability-identifier-naming)
608    {
609        return vector_bb_.end();
610    }
611    auto end() const  // NOLINT(readability-identifier-naming)
612    {
613        return vector_bb_.end();
614    }
615
616    void Dump(std::ostream *out) const;
617
618    Loop *GetRootLoop()
619    {
620        return root_loop_;
621    }
622    const Loop *GetRootLoop() const
623    {
624        return root_loop_;
625    }
626
627    void SetRootLoop(Loop *root_loop)
628    {
629        root_loop_ = root_loop;
630    }
631
632    void SetHasIrreducibleLoop(bool has_irr_loop)
633    {
634        FlagIrredicibleLoop::Set(has_irr_loop, &bit_fields_);
635    }
636
637    void SetHasInfiniteLoop(bool has_inf_loop)
638    {
639        FlagInfiniteLoop::Set(has_inf_loop, &bit_fields_);
640    }
641
642    void SetHasFloatRegs()
643    {
644        FlagFloatRegs::Set(true, &bit_fields_);
645    }
646
647    bool HasLoop() const;
648    bool HasIrreducibleLoop() const;
649    bool HasInfiniteLoop() const;
650    bool HasFloatRegs() const;
651
652    /**
653     * Try-catch info
654     * Vector of begin try-blocks in order they were declared in the bytecode
655     */
656    void AppendTryBeginBlock(const BasicBlock *block)
657    {
658        try_begin_blocks_.push_back(block);
659    }
660
661    void EraseTryBeginBlock(const BasicBlock *block)
662    {
663        auto it = std::find(try_begin_blocks_.begin(), try_begin_blocks_.end(), block);
664        if (it == try_begin_blocks_.end()) {
665            ASSERT(false && "Trying to remove non try_begin block");
666            return;
667        }
668        try_begin_blocks_.erase(it);
669    }
670
671    const auto &GetTryBeginBlocks() const
672    {
673        return try_begin_blocks_;
674    }
675
676    void AppendThrowableInst(const Inst *inst, BasicBlock *catch_handler)
677    {
678        auto it = throwable_insts_.emplace(inst, GetAllocator()->Adapter()).first;
679        it->second.push_back(catch_handler);
680    }
681
682    bool IsInstThrowable(const Inst *inst) const
683    {
684        return throwable_insts_.count(inst) > 0;
685    }
686
687    void RemoveThrowableInst(const Inst *inst);
688    void ReplaceThrowableInst(Inst *old_inst, Inst *new_inst);
689
690    const auto &GetThrowableInstHandlers(const Inst *inst) const
691    {
692        ASSERT(IsInstThrowable(inst));
693        return throwable_insts_.at(inst);
694    }
695
696    void ClearTryCatchInfo()
697    {
698        throwable_insts_.clear();
699        try_begin_blocks_.clear();
700    }
701
702    void DumpThrowableInsts(std::ostream *out) const;
703
704    /**
705     * Run pass specified by template argument T.
706     * Optimization passes might take additional arguments that will passed to Optimization's constructor.
707     * Analyses can't take additional arguments.
708     * @tparam T Type of pass
709     * @param args Additional arguments for optimizations passes
710     * @return true if pass was successful
711     */
712    template <typename T, typename... Args>
713    bool RunPass(Args... args)
714    {
715        ASSERT(GetPassManager());
716        return pass_manager_.RunPass<T>(std::forward<Args>(args)...);
717    }
718    template <typename T, typename... Args>
719    bool RunPass(Args... args) const
720    {
721        ASSERT(GetPassManager());
722        return pass_manager_.RunPass<T>(std::forward<Args>(args)...);
723    }
724
725    template <typename T>
726    bool RunPass(T *pass)
727    {
728        ASSERT(GetPassManager());
729        return pass_manager_.RunPass(pass, GetLocalAllocator()->GetAllocatedSize());
730    }
731
732    /**
733     * Get analysis instance.
734     * All analyses are reside in Graph object in composition relationship.
735     * @tparam T Type of analysis
736     * @return Reference to analysis instance
737     */
738    template <typename T>
739    T &GetAnalysis()
740    {
741        ASSERT(GetPassManager());
742        return GetPassManager()->GetAnalysis<T>();
743    }
744    template <typename T>
745    const T &GetAnalysis() const
746    {
747        ASSERT(GetPassManager());
748        return pass_manager_.GetAnalysis<T>();
749    }
750
751    /**
752     * Same as GetAnalysis but additionaly checck that analysis in valid state.
753     * @tparam T Type of analysis
754     * @return Reference to analysis instance
755     */
756    template <typename T>
757    T &GetValidAnalysis()
758    {
759        RunPass<T>();
760        ASSERT(IsAnalysisValid<T>());
761        return GetAnalysis<T>();
762    }
763    template <typename T>
764    const T &GetValidAnalysis() const
765    {
766        RunPass<T>();
767        ASSERT(IsAnalysisValid<T>());
768        return GetAnalysis<T>();
769    }
770
771    /**
772     * Return true if Analysis valid, false otherwise
773     * @tparam T Type of analysis
774     */
775    template <typename T>
776    bool IsAnalysisValid() const
777    {
778        return GetAnalysis<T>().IsValid();
779    }
780
781    /**
782     * Reset valid state of specified analysis
783     * @tparam T Type of analysis
784     */
785    template <typename T>
786    void InvalidateAnalysis()
787    {
788        ASSERT(GetPassManager());
789        GetPassManager()->GetAnalysis<T>().SetValid(false);
790    }
791
792    /// Accessors to the number of current instruction id.
793    auto GetCurrentInstructionId() const
794    {
795        return instr_current_id_;
796    }
797    auto SetCurrentInstructionId(size_t v)
798    {
799        instr_current_id_ = v;
800    }
801
802    /// RuntimeInterface accessors
803    RuntimeInterface *GetRuntime() const
804    {
805        return runtime_;
806    }
807    void SetRuntime(RuntimeInterface *runtime)
808    {
809        runtime_ = runtime;
810    }
811    auto GetMethod() const
812    {
813        return method_;
814    }
815    auto SetMethod(RuntimeInterface::MethodPtr method)
816    {
817        method_ = method;
818    }
819
820    void ResetParameterInfo();
821
822    EventWriter &GetEventWriter()
823    {
824        return event_writer_;
825    }
826
827    // clang-format off
828
829    /**
830     * Create instruction by opcode
831     */
832    [[nodiscard]] Inst* CreateInst(Opcode opc) const
833    {
834        switch (opc) {
835// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
836#define INST_DEF(OPCODE, BASE, ...)                                      \
837            case Opcode::OPCODE: {                                       \
838                auto inst = Inst::New<BASE>(ALLOCATOR, Opcode::OPCODE);  \
839                inst->SetId(instr_current_id_++);                        \
840                return inst;                                             \
841                }
842                OPCODE_LIST(INST_DEF)
843
844#undef INST_DEF
845            default:
846                return nullptr;
847        }
848    }
849    /**
850     * Define creation methods for all opcodes
851     */
852// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
853#define INST_DEF(OPCODE, BASE, ...)                                               \
854    template <typename... Args>                                                   \
855    [[nodiscard]] BASE* CreateInst##OPCODE(Args&&... args) const {                \
856        auto inst = Inst::New<BASE>(ALLOCATOR, Opcode::OPCODE, std::forward<Args>(args)...);  \
857        inst->SetId(instr_current_id_++); \
858        return inst; \
859    }
860    OPCODE_LIST(INST_DEF)
861
862#undef INST_DEF
863    // clang-format on
864
865    uint32_t GetBitFields()
866    {
867        return bit_fields_;
868    }
869
870    void SetBitFields(uint32_t bit_fields)
871    {
872        bit_fields_ = bit_fields;
873    }
874
875    bool NeedCleanup() const
876    {
877        return FlagNeedCleanup::Get(bit_fields_);
878    }
879
880    void SetNeedCleanup(bool v)
881    {
882        FlagNeedCleanup::Set(v, &bit_fields_);
883    }
884
885    bool IsOsrMode() const
886    {
887        return mode_.IsOsr();
888    }
889
890    bool IsBytecodeOptimizer() const
891    {
892        return mode_.IsBytecodeOpt();
893    }
894
895    bool IsDynamicMethod() const
896    {
897        return mode_.IsDynamicMethod();
898    }
899
900    bool SupportManagedCode() const
901    {
902        return mode_.SupportManagedCode();
903    }
904
905    GraphMode GetMode() const
906    {
907        return mode_;
908    }
909
910    void SetMode(GraphMode mode)
911    {
912        mode_ = mode;
913    }
914
915#ifndef NDEBUG
916    compiler::inst_modes::Mode GetCompilerMode()
917    {
918        if (IsBytecodeOptimizer()) {
919            return compiler::inst_modes::BYTECODE_OPT;
920        }
921        if (SupportManagedCode()) {
922            return compiler::inst_modes::JIT_AOT;
923        }
924        return compiler::inst_modes::IRTOC;
925    }
926#endif
927
928    void AddSingleImplementationMethod(RuntimeInterface::MethodPtr method)
929    {
930        single_implementation_list_.push_back(method);
931    }
932
933    void SetDynamicMethod()
934    {
935        mode_.SetDynamicMethod(true);
936    }
937
938    auto &GetSingleImplementationList()
939    {
940        return single_implementation_list_;
941    }
942
943    Graph *GetParentGraph()
944    {
945        return parent_graph_;
946    }
947
948    Graph *GetOutermostParentGraph()
949    {
950        auto graph = this;
951        while (graph->GetParentGraph() != nullptr) {
952            graph = graph->GetParentGraph();
953        }
954        return graph;
955    }
956
957    void SetVRegsCount(size_t count)
958    {
959        vregs_count_ = count;
960    }
961
962    size_t GetVRegsCount() const
963    {
964        return vregs_count_;
965    }
966
967    int64_t GetBranchCounter(const BasicBlock *block, bool true_succ);
968
969    /**
970     * This class provides methods for ranged-based `for` loop over all parameters in the graph.
971     */
972    class ParameterList {
973    public:
974        class Iterator {
975        public:
976            explicit Iterator(Inst *inst) : inst_(inst) {}
977
978            Iterator &operator++()
979            {
980                for (inst_ = inst_->GetNext(); inst_ != nullptr && inst_->GetOpcode() != Opcode::Parameter;
981                     inst_ = inst_->GetNext()) {
982                }
983                return *this;
984            }
985            bool operator!=(const Iterator &other)
986            {
987                return inst_ != other.inst_;
988            }
989            Inst *operator*()
990            {
991                return inst_;
992            }
993            Inst *operator->()
994            {
995                return inst_;
996            }
997
998        private:
999            Inst *inst_ {nullptr};
1000        };
1001
1002        explicit ParameterList(const Graph *graph) : graph_(graph) {}
1003
1004        // NOLINTNEXTLINE(readability-identifier-naming)
1005        Iterator begin();
1006        // NOLINTNEXTLINE(readability-identifier-naming)
1007        static Iterator end()
1008        {
1009            return Iterator(nullptr);
1010        }
1011
1012    private:
1013        const Graph *graph_ {nullptr};
1014    };
1015
1016    /**
1017     * Get list of all parameters
1018     * @return instance of the ParameterList class
1019     */
1020    ParameterList GetParameters() const
1021    {
1022        return ParameterList(this);
1023    }
1024
1025    void InitDefaultLocations();
1026
1027private:
1028    void AddConstInStartBlock(ConstantInst *const_inst);
1029
1030    NO_MOVE_SEMANTIC(Graph);
1031    NO_COPY_SEMANTIC(Graph);
1032
1033private:
1034    ArenaAllocator *const ALLOCATOR;
1035    ArenaAllocator *const LOCAL_ALLOCATOR;
1036
1037    Arch arch_ {RUNTIME_ARCH};
1038
1039    // List of blocks in insertion order.
1040    ArenaVector<BasicBlock *> vector_bb_;
1041    BasicBlock *start_block_ {nullptr};
1042    BasicBlock *end_block_ {nullptr};
1043
1044    Loop *root_loop_ {nullptr};
1045
1046    uint32_t bit_fields_ {0};
1047    using FlagDFConstruct = BitField<bool, 0, 1>;
1048    using FlagNeedCleanup = FlagDFConstruct::NextFlag;
1049    using FlagIrredicibleLoop = FlagNeedCleanup::NextFlag;
1050    using FlagInfiniteLoop = FlagIrredicibleLoop::NextFlag;
1051    using FlagFloatRegs = FlagInfiniteLoop::NextFlag;
1052    using FlagDefaultLocationsInit = FlagFloatRegs::NextFlag;
1053#ifndef NDEBUG
1054    using FlagRegallocApplied = FlagDefaultLocationsInit::NextFlag;
1055    using FlagRegaccallocApplied = FlagRegallocApplied::NextFlag;
1056    using FlagInliningComplete = FlagRegaccallocApplied::NextFlag;
1057    using FlagSchedulerComplete = FlagInliningComplete::NextFlag;
1058    using FlagLowLevelInstnsEnabled = FlagSchedulerComplete::NextFlag;
1059#endif  // NDEBUG
1060
1061    // codegen data
1062    EncodeDataType data_;
1063    Span<const uint8_t> code_info_data_;
1064    ArenaVector<bool> *used_regs_ {nullptr};
1065    ArenaVector<bool> *used_vregs_ {nullptr};
1066
1067    // TODO (a.popov) Replace by ArenaMap from throwable_inst* to try_inst*
1068    ArenaMap<const Inst *, ArenaVector<BasicBlock *>> throwable_insts_;
1069
1070    mutable size_t instr_current_id_ {0};
1071    // first constant instruction in graph !TODO rewrite it to hash-map
1072    ConstantInst *first_const_inst_ {nullptr};
1073    Inst *nullptr_inst_ {nullptr};
1074
1075    RuntimeInterface *runtime_ {nullptr};
1076    RuntimeInterface::MethodPtr method_ {nullptr};
1077
1078    ParameterInfo *param_info_ {nullptr};
1079
1080    mutable PassManager pass_manager_;
1081    EventWriter event_writer_;
1082
1083    GraphMode mode_;
1084
1085    ArenaVector<RuntimeInterface::MethodPtr> single_implementation_list_;
1086    ArenaVector<const BasicBlock *> try_begin_blocks_;
1087    ArenaVector<ConstantInst *> spilled_constants_;
1088    // Graph that inlines this graph
1089    Graph *parent_graph_ {nullptr};
1090    // Number of used stack slots
1091    uint32_t stack_slot_count_ {0};
1092    // Number of used stack slots for parameters
1093    uint32_t param_slots_count_ {0};
1094    // First language extension slot
1095    uint32_t ext_stack_slot_ {0};
1096    // Number of the virtual registers used in the compiled method (inlined methods aren't included).
1097    uint32_t vregs_count_ {0};
1098};
1099
1100class MarkerHolder {
1101public:
1102    NO_COPY_SEMANTIC(MarkerHolder);
1103    NO_MOVE_SEMANTIC(MarkerHolder);
1104
1105    explicit MarkerHolder(const Graph *graph) : graph_(graph), marker_(graph->NewMarker())
1106    {
1107        ASSERT(marker_ != UNDEF_MARKER);
1108    }
1109
1110    ~MarkerHolder()
1111    {
1112        graph_->EraseMarker(marker_);
1113    }
1114
1115    Marker GetMarker()
1116    {
1117        return marker_;
1118    }
1119
1120private:
1121    const Graph *graph_;
1122    Marker marker_ {UNDEF_MARKER};
1123};
1124
1125template <typename T>
1126ConstantInst *Graph::FindOrCreateConstant(T value)
1127{
1128    bool is_support_int32 = IsBytecodeOptimizer();
1129    if (first_const_inst_ == nullptr) {
1130        first_const_inst_ = CreateInstConstant(value, is_support_int32);
1131        AddConstInStartBlock(first_const_inst_);
1132        return first_const_inst_;
1133    }
1134    ConstantInst *current_const = first_const_inst_;
1135    ConstantInst *prev_const = nullptr;
1136    while (current_const != nullptr) {
1137        if (current_const->IsEqualConst(value, is_support_int32)) {
1138            return current_const;
1139        }
1140        prev_const = current_const;
1141        current_const = current_const->GetNextConst();
1142    }
1143    ASSERT(prev_const != nullptr);
1144    auto *new_const = CreateInstConstant(value, is_support_int32);
1145    AddConstInStartBlock(new_const);
1146
1147    prev_const->SetNextConst(new_const);
1148    return new_const;
1149}
1150
1151void InvalidateBlocksOrderAnalyzes(Graph *graph);
1152void MarkLoopExits(const Graph *graph, Marker marker);
1153void RemovePredecessorUpdateDF(BasicBlock *block, BasicBlock *rm_pred);
1154std::string GetMethodFullName(const Graph *graph, RuntimeInterface::MethodPtr method);
1155}  // namespace panda::compiler
1156
1157#endif  // COMPILER_OPTIMIZER_IR_GRAPH_H
1158