/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ECMASCRIPT_COMPILER_PASS_H
#define ECMASCRIPT_COMPILER_PASS_H

#include "ecmascript/compiler/array_bounds_check_elimination.h"
#include "ecmascript/compiler/async_function_lowering.h"
#include "ecmascript/compiler/bytecode_circuit_builder.h"
#include "ecmascript/compiler/combined_pass_visitor.h"
#include "ecmascript/compiler/common_stubs.h"
#include "ecmascript/compiler/compiler_log.h"
#include "ecmascript/compiler/dead_code_elimination.h"
#include "ecmascript/compiler/constant_folding.h"
#include "ecmascript/compiler/early_elimination.h"
#include "ecmascript/compiler/graph_editor.h"
#include "ecmascript/compiler/graph_linearizer.h"
#include "ecmascript/compiler/later_elimination.h"
#include "ecmascript/compiler/mcr_lowering.h"
#include "ecmascript/compiler/lexical_env_specialization_pass.h"
#include "ecmascript/compiler/llvm_codegen.h"
#include "ecmascript/compiler/loop_analysis.h"
#include "ecmascript/compiler/loop_peeling.h"
#include "ecmascript/compiler/native_inline_lowering.h"
#include "ecmascript/compiler/ntype_bytecode_lowering.h"
#include "ecmascript/compiler/ntype_hcr_lowering.h"
#include "ecmascript/compiler/number_speculative_runner.h"
#include "ecmascript/compiler/scheduler.h"
#include "ecmascript/compiler/string_builder_optimizer.h"
#include "ecmascript/compiler/slowpath_lowering.h"
#include "ecmascript/compiler/state_split_linearizer.h"
#include "ecmascript/compiler/ts_class_analysis.h"
#include "ecmascript/compiler/ts_inline_lowering.h"
#include "ecmascript/compiler/typed_bytecode_lowering.h"
#include "ecmascript/compiler/ts_hcr_opt_pass.h"
#include "ecmascript/compiler/type_inference/global_type_infer.h"
#include "ecmascript/compiler/type_inference/initialization_analysis.h"
#include "ecmascript/compiler/type_inference/pgo_type_infer.h"
#include "ecmascript/compiler/typed_hcr_lowering.h"
#include "ecmascript/compiler/value_numbering.h"
#include "ecmascript/compiler/instruction_combine.h"
#include "ecmascript/compiler/verifier.h"
#include "ecmascript/js_runtime_options.h"

#ifdef COMPILE_MAPLE
#include "ecmascript/compiler/codegen/maple/litecg_codegen.h"
#include "litecg.h"
#include "lmir_builder.h"
#endif

namespace panda::ecmascript::kungfu {
class PassContext;

class PassData {
public:
    PassData(BytecodeCircuitBuilder *builder, Circuit *circuit, PassContext *ctx, CompilerLog *log,
             std::string methodName, MethodInfo *methodInfo = nullptr, bool hasTypes = false,
             const CString &recordName = "", MethodLiteral *methodLiteral = nullptr,
             uint32_t methodOffset = 0, NativeAreaAllocator *allocator = nullptr,
             PGOProfilerDecoder *decoder = nullptr, PassOptions *passOptions = nullptr)
        : builder_(builder), circuit_(circuit), ctx_(ctx), log_(log), methodName_(methodName),
          methodInfo_(methodInfo), hasTypes_(hasTypes), recordName_(recordName), methodLiteral_(methodLiteral),
          methodOffset_(methodOffset), allocator_(allocator), decoder_(decoder), passOptions_(passOptions)
    {
    }

    virtual ~PassData() = default;

    const ControlFlowGraph &GetConstScheduleResult() const
    {
        return cfg_;
    }

    ControlFlowGraph &GetCfg()
    {
        return cfg_;
    }

    virtual Circuit* GetCircuit() const
    {
        return circuit_;
    }

    BytecodeCircuitBuilder* GetBuilder() const
    {
        return builder_;
    }

    PassContext* GetPassContext() const
    {
        return ctx_;
    }

    CompilationConfig* GetCompilerConfig() const
    {
        return ctx_->GetCompilerConfig();
    }

    TSManager* GetTSManager() const
    {
        return ctx_->GetTSManager();
    }

    PGOTypeManager* GetPTManager() const
    {
        return ctx_->GetPTManager();
    }

    const JSPandaFile *GetJSPandaFile() const
    {
        return ctx_->GetJSPandaFile();
    }

    IRModule* GetAotModule() const
    {
        return ctx_->GetAOTModule();
    }

    CompilerLog* GetLog() const
    {
        return log_;
    }

    const std::string& GetMethodName() const
    {
        return methodName_;
    }

    const MethodLiteral* GetMethodLiteral() const
    {
        return methodLiteral_;
    }

    uint32_t GetMethodOffset() const
    {
        return methodOffset_;
    }

    MethodInfo* GetMethodInfo() const
    {
        return methodInfo_;
    }

    size_t GetMethodInfoIndex() const
    {
        return methodInfo_->GetMethodInfoIndex();
    }

    bool HasTypes() const
    {
        return hasTypes_;
    }

    const CString &GetRecordName() const
    {
        return recordName_;
    }

    NativeAreaAllocator* GetNativeAreaAllocator() const
    {
        return allocator_;
    }

    PGOProfilerDecoder *GetPGOProfilerDecoder() const
    {
        return decoder_;
    }

    PassOptions *GetPassOptions() const
    {
        return passOptions_;
    }

    bool IsTypeAbort() const
    {
        if (hasTypes_) {
            // A ts method which has low type percent and not marked as a resolved method
            // should be skipped from full compilation.
            if (methodInfo_->IsTypeInferAbort() && !methodInfo_->IsResolvedMethod()) {
                return true;
            }
        } else {
            // For js method, type infer pass will be skipped and it don't have a type percent.
            // If we set an non zero type threshold, js method will be skipped from full compilation.
            // The default Type threshold is -1.
            if (ctx_->GetTSManager()->GetTypeThreshold() >= 0) {
                return true;
            }
        }
        // when a method will be full compiled, we should confirm its TypeInferAbortBit to be false
        // maybe it used to be true in the first round of compilation.
        methodInfo_->SetTypeInferAbort(false);
        log_->AddCompiledMethod(methodName_, recordName_);
        return false;
    }

    void AbortCompilation()
    {
        ctx_->GetBytecodeInfo().AddSkippedMethod(methodOffset_);
        methodInfo_->SetIsCompiled(false);
        log_->RemoveCompiledMethod(methodName_, recordName_);
    }

    void MarkAsTypeAbort()
    {
        methodInfo_->SetTypeInferAbort(true);
    }

private:
    BytecodeCircuitBuilder *builder_ {nullptr};
    Circuit *circuit_ {nullptr};
    ControlFlowGraph cfg_;
    PassContext *ctx_ {nullptr};
    CompilerLog *log_ {nullptr};
    std::string methodName_;
    MethodInfo *methodInfo_ {nullptr};
    bool hasTypes_;
    const CString &recordName_;
    MethodLiteral *methodLiteral_ {nullptr};
    uint32_t methodOffset_;
    NativeAreaAllocator *allocator_ {nullptr};
    PGOProfilerDecoder *decoder_ {nullptr};
    PassOptions *passOptions_ {nullptr};
};

template<typename T1>
class PassRunner {
public:
    explicit PassRunner(T1* data) : data_(data) {}
    virtual ~PassRunner() = default;
    template<typename T2, typename... Args>
    bool RunPass(Args... args)
    {
        T2 pass;
        return pass.Run(data_, std::forward<Args>(args)...);
    }

private:
    T1* data_;
};

class TypeInferPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (passOptions != nullptr && !passOptions->EnableTypeInfer()) {
            return false;
        }
        TimeScope timescope("TypeInferPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->GetEnableMethodLog() && data->GetLog()->OutputType();
        GlobalTypeInfer globalTypeInfer(data->GetPassContext(), data->GetMethodOffset(), data->GetRecordName(),
                                        data->GetPGOProfilerDecoder(), passOptions->EnableOptTrackField(),
                                        enableLog, data->HasTypes());
        globalTypeInfer.ProcessTypeInference(data->GetBuilder(), data->GetCircuit());
        return true;
    }
};

class PGOTypeInferPass {
public:
    bool Run(PassData* data)
    {
        TimeScope timescope("PGOTypeInferPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->GetEnableMethodLog() && data->GetLog()->OutputType();
        Chunk chunk(data->GetNativeAreaAllocator());
        PGOTypeInfer pgoTypeInfer(data->GetCircuit(), data->GetTSManager(), data->GetBuilder(),
                                  data->GetMethodName(), &chunk, enableLog);
        pgoTypeInfer.Run();
        return true;
    }
};

class TSClassAnalysisPass {
public:
    bool Run(PassData *data)
    {
        TimeScope timescope("TSClassAnalysisPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        TSClassAnalysis analyzer(data->GetPassContext()->GetTSManager());
        analyzer.Run();
        return true;
    }
};

class TypeBytecodeLoweringPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableTypeLowering()) {
            return false;
        }
        TimeScope timescope("TypeBytecodeLoweringPass", data->GetMethodName(),
            data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        bool enableTypeLog = data->GetLog()->GetEnableMethodLog() && data->GetLog()->OutputType();
        Chunk chunk(data->GetNativeAreaAllocator());
        TypedBytecodeLowering lowering(data->GetCircuit(), data->GetPassContext(), &chunk,
                                      enableLog,
                                      enableTypeLog,
                                      data->GetMethodName(),
                                      passOptions->EnableLoweringBuiltin(),
                                      data->GetRecordName());
        bool success = lowering.RunTypedBytecodeLowering();
        if (!success) {
            data->MarkAsTypeAbort();
        }
        CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
        DeadCodeElimination deadCodeElimination(data->GetCircuit(), &visitor, &chunk);
        TSHCROptPass optimization(data->GetCircuit(), &visitor, &chunk, data->GetPassContext(), enableLog,
                                  data->GetMethodName());

        visitor.AddPass(&optimization);
        visitor.AddPass(&deadCodeElimination);
        visitor.VisitGraph();
        visitor.PrintLog("TSHCROptPass");
        return true;
    }
};

class NTypeBytecodeLoweringPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableTypeLowering()) {
            return false;
        }
        TimeScope timescope("NTypeBytecodeLoweringPass", data->GetMethodName(),
            data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        NTypeBytecodeLowering lowering(data->GetCircuit(), data->GetPassContext(), data->GetTSManager(),
            enableLog, data->GetMethodName());
        lowering.RunNTypeBytecodeLowering();
        Chunk chunk(data->GetNativeAreaAllocator());
        CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
        DeadCodeElimination deadCodeElimination(data->GetCircuit(), &visitor, &chunk);
        visitor.AddPass(&deadCodeElimination);
        visitor.VisitGraph();
        return true;
    }
};

class StringOptimizationPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableOptString()) {
            return false;
        }
        TimeScope timescope("StringOptimizationPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        Chunk chunk(data->GetNativeAreaAllocator());
        StringBuilderOptimizer stringBuilder(data->GetCircuit(),
                                             enableLog,
                                             data->GetMethodName(),
                                             data->GetCompilerConfig(),
                                             &chunk);
        stringBuilder.Run();
        return true;
    }
};

class TypeHCRLoweringPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableTypeLowering()) {
            return false;
        }
        TimeScope timescope("TypeHCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        Chunk chunk(data->GetNativeAreaAllocator());
        CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
        TypedHCRLowering lowering(data->GetCircuit(),
                                 &visitor,
                                 data->GetCompilerConfig(),
                                 data->GetTSManager(),
                                 &chunk,
                                 passOptions->EnableLoweringBuiltin());
        visitor.AddPass(&lowering);
        visitor.VisitGraph();
        visitor.PrintLog("TypedHCRLowering");
        return true;
    }
};

class NTypeHCRLoweringPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableTypeLowering()) {
            return false;
        }
        TimeScope timescope("NTypeHCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        Chunk chunk(data->GetNativeAreaAllocator());
        CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
        NTypeHCRLowering lowering(data->GetCircuit(), &visitor, data->GetPassContext(),
                                  data->GetRecordName(), &chunk);
        visitor.AddPass(&lowering);
        visitor.VisitGraph();
        visitor.PrintLog("NTypeHCRLowering");
        return true;
    }
};

class LCRLoweringPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableTypeLowering()) {
            return false;
        }
        TimeScope timescope("LCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        Chunk chunk(data->GetNativeAreaAllocator());
        CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
        MCRLowering lowering(data->GetCircuit(), &visitor, data->GetCompilerConfig(), &chunk);
        visitor.AddPass(&lowering);
        visitor.VisitGraph();
        visitor.PrintLog("MCRLowering");
        return true;
    }
};

class TSInlineLoweringPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableOptInlining() || !passOptions->EnableTypeLowering()) {
            return false;
        }
        TimeScope timescope("TSInlineLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        TSInlineLowering inlining(data->GetCircuit(), data->GetPassContext(), enableLog, data->GetMethodName(),
                                  data->GetNativeAreaAllocator(), passOptions, data->GetMethodOffset());
        inlining.RunTSInlineLowering();
        if (passOptions->EnableLexenvSpecialization()) {
            Chunk chunk(data->GetNativeAreaAllocator());
            {
                CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
                GetEnvSpecializationPass getEnvSpecializationPass(data->GetCircuit(), &visitor, &chunk);
                visitor.AddPass(&getEnvSpecializationPass);
                visitor.VisitGraph();
                visitor.PrintLog("getEnvSpecializationPass");
            }
            {
                CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
                LexicalEnvSpecializationPass lexicalEnvSpecializationPass(data->GetCircuit(), &visitor, &chunk,
                                                                          enableLog);
                visitor.AddPass(&lexicalEnvSpecializationPass);
                visitor.VisitGraph();
                visitor.PrintLog("lexicalEnvSpecialization");
                lexicalEnvSpecializationPass.PrintSpecializeId();
            }
        }

        if (passOptions->EnableInlineNative()) {
            NativeInlineLowering nativeInline(data->GetCircuit(), data->GetPassContext(), enableLog,
                                              data->GetMethodName());
            nativeInline.RunNativeInlineLowering();
        }
        return true;
    }
};

class SlowPathLoweringPass {
public:
    bool Run(PassData* data)
    {
        TimeScope timescope("SlowPathLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        SlowPathLowering lowering(data->GetCircuit(), data->GetCompilerConfig(), data->GetTSManager(),
                                  data->GetMethodLiteral(), enableLog, data->GetMethodName());
        lowering.CallRuntimeLowering();
        return true;
    }
};

class RunFlowCyclesVerifierPass {
public:
    bool Run(PassData* data)
    {
        TimeScope timescope("FlowCyclesVerifierPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        bool hasFlowCycle = Verifier::RunFlowCyclesFind(data->GetCircuit());
        if (hasFlowCycle) {
            LOG_FULL(FATAL) << "FlowCyclesVerifierPass fail";
            UNREACHABLE();
        }
        return !hasFlowCycle;
    }
};

class VerifierPass {
public:
    bool Run(PassData* data)
    {
        TimeScope timescope("VerifierPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        bool isQualified = Verifier::Run(data->GetCircuit(), data->GetMethodName(), enableLog);
        if (!isQualified) {
            LOG_FULL(FATAL) << "VerifierPass fail";
            UNREACHABLE();
        }
        return isQualified;
    }
};

class NumberSpeculativePass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableTypeLowering()) {
            return false;
        }
        TimeScope timescope("NumberSpeculativePass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        Chunk chunk(data->GetNativeAreaAllocator());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        bool enableArrayBoundsCheckElimination = passOptions->EnableArrayBoundsCheckElimination();
        CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
        NumberSpeculativeRunner(data->GetCircuit(), enableLog, enableArrayBoundsCheckElimination,
                                data->GetMethodName(), &chunk).Run();
        return true;
    }
};

class ConstantFoldingPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableOptConstantFolding()) {
            return false;
        }
        TimeScope timescope("ConstantFoldingPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        Chunk chunk(data->GetNativeAreaAllocator());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
        ConstantFolding constantFolding(data->GetCircuit(), &visitor, data->GetCompilerConfig(), enableLog,
                                        data->GetMethodName(), &chunk);
        visitor.AddPass(&constantFolding);
        visitor.VisitGraph();
        constantFolding.Print();
        return true;
    }
};

class LoopOptimizationPass {
public:
    bool Run(PassData* data)
    {
        TimeScope timescope("LoopOptimizationPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        Chunk chunk(data->GetNativeAreaAllocator());
        LoopAnalysis loopAnalysis(data->GetBuilder(), data->GetCircuit(), &chunk);
        loopAnalysis.Run();
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        for (auto loopInfo : loopAnalysis.GetLoopTree()) {
            loopAnalysis.CollectLoopBody(loopInfo);
            if (enableLog) {
                loopAnalysis.PrintLoop(loopInfo);
            }
            if (data->GetPassOptions()->EnableOptLoopPeeling()) {
                LoopPeeling(data->GetBuilder(), data->GetCircuit(), enableLog,
                            data->GetMethodName(), &chunk, loopInfo).Peel();
            }
        }
        loopAnalysis.LoopExitElimination();
        return true;
    }
};

class RedundantPhiEliminationPass {
public:
    bool Run(PassData* data)
    {
        TimeScope timescope("RedundantPhiEliminationPass", data->GetMethodName(),
            data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        GraphEditor::EliminateRedundantPhi(data->GetCircuit(), enableLog, data->GetMethodName());
        return true;
    }
};

class EarlyEliminationPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableTypeLowering() || !passOptions->EnableEarlyElimination()) {
            return false;
        }
        TimeScope timescope("EarlyEliminationPass", data->GetMethodName(),
                            data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog() || data->GetLog()->EnableMethodASMLog();
        Chunk chunk(data->GetNativeAreaAllocator());
        CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
        EarlyElimination earlyElimination(data->GetCircuit(), &visitor, &chunk);
        visitor.AddPass(&earlyElimination);
        visitor.VisitGraph();
        visitor.PrintLog("early elimination");
        return true;
    }
};

class LaterEliminationPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableTypeLowering() || !passOptions->EnableLaterElimination()) {
            return false;
        }
        TimeScope timescope("LaterEliminationPass", data->GetMethodName(),
                            data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog() || data->GetLog()->EnableMethodASMLog();
        Chunk chunk(data->GetNativeAreaAllocator());
        CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
        LaterElimination laterElimination(data->GetCircuit(), &visitor, &chunk);
        visitor.AddPass(&laterElimination);
        visitor.VisitGraph();
        visitor.PrintLog("later elimination");
        return true;
    }
};

class ValueNumberingPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableTypeLowering() || !passOptions->EnableValueNumbering()) {
            return false;
        }
        JSRuntimeOptions runtimeOption = data->GetPassContext()->GetEcmaVM()->GetJSOptions();
        TimeScope timescope("ValueNumberingPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        Chunk chunk(data->GetNativeAreaAllocator());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
        ValueNumbering valueNumbering(data->GetCircuit(), &visitor, &chunk,
                                      runtimeOption.IsEnableNewValueNumbering(),
                                      runtimeOption.GetTraceValueNumbering());
        visitor.AddPass(&valueNumbering);
        visitor.VisitGraph();
        visitor.PrintLog("value numbering");
        return true;
    }
};

class InstructionCombinePass {
public:
    bool Run(PassData *data)
    {
        JSRuntimeOptions runtimeOption = data->GetPassContext()->GetEcmaVM()->GetJSOptions();
        if (runtimeOption.IsEnableInstrcutionCombine()) {
            TimeScope timescope("InstructionCombinePass", data->GetMethodName(), data->GetMethodOffset(),
                                data->GetLog());
            Chunk chunk(data->GetNativeAreaAllocator());
            bool enableLog = data->GetLog()->EnableMethodCIRLog();
            CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk);
            InstructionCombine instructionCombine(data->GetCircuit(), &visitor, &chunk,
                                                  runtimeOption.GetTraceInstructionCombine());
            visitor.AddPass(&instructionCombine);
            visitor.VisitGraph();
            visitor.PrintLog("Instruction Combine");
        }
        return true;
    }
};

class SchedulingPass {
public:
    bool Run(PassData* data)
    {
        TimeScope timescope("SchedulingPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        Scheduler::Run(data->GetCircuit(), data->GetCfg(), data->GetMethodName(), enableLog);
        return true;
    }
};

class StateSplitLinearizerPass {
public:
    bool Run(PassData* data)
    {
        PassOptions *passOptions = data->GetPassOptions();
        if (!passOptions->EnableTypeLowering()) {
            return false;
        }
        TimeScope timescope("StateSplitLinearizerPass", data->GetMethodName(),
                            data->GetMethodOffset(), data->GetLog());
        Chunk chunk(data->GetNativeAreaAllocator());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        StateSplitLinearizer(data->GetCircuit(), nullptr, data->GetCompilerConfig(),
            enableLog, data->GetMethodName(), &chunk).Run();
        return true;
    }
};

class GraphLinearizerPass {
public:
    bool Run(PassData* data)
    {
        TimeScope timescope("GraphLinearizerPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        Chunk chunk(data->GetNativeAreaAllocator());
        bool enableLog = data->GetLog()->EnableMethodCIRLog();
        bool licm = data->GetPassOptions()->EnableOptLoopInvariantCodeMotion();
        bool liteCG = data->GetTSManager()->GetEcmaVM()->GetJSOptions().IsCompilerEnableLiteCG();
        GraphLinearizer(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk, false, licm, liteCG)
            .Run(data->GetCfg());
        return true;
    }
};

class CGIRGenPass {
public:
    void CreateCodeGen(IRModule *module, bool enableLog)
    {
#ifdef COMPILE_MAPLE
        if (module->GetModuleKind() == MODULE_LLVM) {
            cgImpl_ = std::make_unique<LLVMIRGeneratorImpl>(static_cast<LLVMModule*>(module), enableLog);
        } else {
            cgImpl_ = std::make_unique<LiteCGIRGeneratorImpl>(static_cast<LMIRModule*>(module), enableLog);
        }
#else
        cgImpl_ = std::make_unique<LLVMIRGeneratorImpl>(static_cast<LLVMModule*>(module), enableLog);
#endif
    }
    bool Run(PassData *data)
    {
        auto module = data->GetAotModule();
        TimeScope timescope("CGIRGenPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog() || data->GetLog()->EnableMethodASMLog();
        PassOptions *passOptions = data->GetPassOptions();
        bool enableOptInlining = passOptions->EnableOptInlining() && passOptions->EnableTypeLowering();
        bool enableOptBranchProfiling = passOptions->EnableOptBranchProfiling();
        CreateCodeGen(module, enableLog);
        CodeGenerator codegen(cgImpl_, data->GetMethodName());
        codegen.Run(data->GetCircuit(), data->GetConstScheduleResult(), data->GetCompilerConfig(),
                    data->GetMethodLiteral(), data->GetJSPandaFile(), enableOptInlining, enableOptBranchProfiling);
        return true;
    }
private:
    std::unique_ptr<CodeGeneratorImpl> cgImpl_ {nullptr};
};

class AsyncFunctionLoweringPass {
public:
    bool Run(PassData* data)
    {
        TimeScope timescope("AsyncFunctionLoweringPass", data->GetMethodName(),
                            data->GetMethodOffset(), data->GetLog());
        bool enableLog = data->GetLog()->EnableMethodCIRLog() || data->GetLog()->EnableMethodASMLog();
        AsyncFunctionLowering lowering(data->GetBuilder(), data->GetCircuit(), data->GetCompilerConfig(),
                                       enableLog, data->GetMethodName());
        if (lowering.IsAsyncRelated()) {
            lowering.ProcessAll();
        }
        return true;
    }

private:
    bool IsFunctionMain(PassData* data)
    {
        auto methodName = data->GetMethodName();
        auto pos = methodName.find(JSPandaFile::ENTRY_FUNCTION_NAME);
        if (pos != std::string::npos) {
            return true;
        }
        return false;
    }
};
} // namespace panda::ecmascript::kungfu
#endif