1/*
2 * Copyright (c) 2021-2024 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 "ecmascript/compiler/stub_compiler.h"
17
18#include "ecmascript/base/config.h"
19#include "ecmascript/base/string_helper.h"
20#include "ecmascript/compiler/codegen/llvm/llvm_codegen.h"
21#include "ecmascript/compiler/file_generators.h"
22#include "ecmascript/compiler/interpreter_stub-inl.h"
23#include "ecmascript/compiler/pass.h"
24#include "ecmascript/compiler/scheduler.h"
25#include "ecmascript/compiler/stub.h"
26#include "ecmascript/compiler/stub_builder-inl.h"
27#include "ecmascript/compiler/verifier.h"
28#include "ecmascript/js_runtime_options.h"
29#include "ecmascript/log.h"
30#include "ecmascript/napi/include/jsnapi.h"
31#include "ecmascript/compiler/baseline/baseline_stub_csigns.h"
32
33namespace panda::ecmascript::kungfu {
34class StubPassData : public PassData {
35public:
36    StubPassData(Stub *stub, LLVMModule *module, CompilerLog *log)
37        : PassData(nullptr, nullptr, nullptr, log, "stubs"),
38          cfg_(module->GetTripleStr()),
39          module_(module),
40          stub_(stub) {}
41    ~StubPassData() = default;
42
43    const CompilationConfig *GetCompilationConfig() const
44    {
45        return &cfg_;
46    }
47
48    Circuit *GetCircuit() const
49    {
50        return stub_->GetCircuit();
51    }
52
53    LLVMModule *GetStubModule() const
54    {
55        return module_;
56    }
57
58    Stub *GetStub() const
59    {
60        return stub_;
61    }
62
63private:
64    CompilationConfig cfg_;
65    LLVMModule *module_;
66    Stub *stub_;
67};
68
69class StubBuildCircuitPass {
70public:
71    bool Run(StubPassData *data)
72    {
73        auto stub = data->GetStub();
74        LOG_COMPILER(INFO) << "Stub Name: " << stub->GetMethodName();
75        stub->GenerateCircuit(data->GetCompilationConfig());
76        return true;
77    }
78};
79
80class StubLLVMIRGenPass {
81public:
82    void CreateCodeGen(LLVMModule *module, bool enableLog)
83    {
84        llvmImpl_ = std::make_unique<LLVMIRGeneratorImpl>(module, enableLog);
85    }
86
87    bool Run(StubPassData *data, size_t index)
88    {
89        bool enableLog = data->GetLog()->EnableMethodCIRLog() || data->GetLog()->OutputASM();
90        auto stubModule = data->GetStubModule();
91        CreateCodeGen(stubModule, enableLog);
92        CodeGenerator codegen(llvmImpl_, "stubs");
93        codegen.RunForStub(data->GetCircuit(), data->GetConstScheduleResult(), index, data->GetCompilationConfig());
94        return true;
95    }
96private:
97    std::unique_ptr<CodeGeneratorImpl> llvmImpl_ {nullptr};
98};
99
100void StubCompiler::RunPipeline(LLVMModule *module, NativeAreaAllocator *allocator) const
101{
102    auto callSigns = module->GetCSigns();
103    CompilerLog *log = GetLog();
104    for (size_t i = 0; i < callSigns.size(); i++) {
105        auto &cs = callSigns[i];
106        Circuit circuit(allocator, module->GetDebugInfo(), cs->GetName().c_str(), module->Is64Bit());
107        Stub stub(cs, &circuit);
108        log->SetStubLog(stub.GetMethodName(), GetLogList());
109
110        StubPassData data(&stub, module, log);
111        PassRunner<StubPassData> pipeline(&data);
112        pipeline.RunPass<StubBuildCircuitPass>();
113        pipeline.RunPass<VerifierPass>();
114        pipeline.RunPass<SchedulingPass>();
115        pipeline.RunPass<StubLLVMIRGenPass>(i);
116    }
117}
118
119void StubCompiler::InitializeCS() const
120{
121    BytecodeStubCSigns::Initialize();
122    CommonStubCSigns::Initialize();
123    BaselineStubCSigns::Initialize();
124    BuiltinsStubCSigns::Initialize();
125    RuntimeStubCSigns::Initialize();
126}
127
128bool StubCompiler::BuildStubModuleAndSave() const
129{
130    if (filePath_.empty()) {
131        return false;
132    }
133
134    InitializeCS();
135    CompilerLog *log = GetLog();
136    MethodLogList *logList = GetLogList();
137
138    NativeAreaAllocator allocator;
139    StubFileGenerator generator(log, logList, triple_, concurrentCompile_);
140
141    LOG_COMPILER(INFO) << "=============== compiling bytecode handler stubs ===============";
142    LOptions stubOp(optLevel_, FPFlag::ELIM_FP, relocMode_);
143    Module* stubM = generator.AddModule(&allocator, "bc_stub", triple_, stubOp, log->OutputASM(), StubFileKind::BC);
144    if (!stubM->IsLLVM()) {
145        LOG_COMPILER(FATAL) << " Stub compiler is not supported for litecg ===============";
146        return false;
147    }
148    RunPipeline(static_cast<LLVMModule*>(stubM->GetModule()), &allocator);
149
150    LOG_COMPILER(INFO) << "=============== compiling common stubs ===============";
151    LOptions comOp(optLevel_, FPFlag::RESERVE_FP, relocMode_);
152    Module* comM = generator.AddModule(&allocator, "com_stub", triple_, comOp, log->OutputASM(), StubFileKind::COM);
153    if (!comM->IsLLVM()) {
154        LOG_COMPILER(FATAL) << " Stub compiler is not supported for litecg ===============";
155        return false;
156    }
157    RunPipeline(static_cast<LLVMModule*>(comM->GetModule()), &allocator);
158
159    LOG_COMPILER(INFO) << "=============== compiling builtins stubs ===============";
160    LOptions builtinOp(optLevel_, FPFlag::RESERVE_FP, relocMode_);
161    Module* builtinM = generator.AddModule(&allocator, "builtin_stub", triple_, builtinOp, log->OutputASM(),
162                                           StubFileKind::BUILTIN);
163    if (!builtinM->IsLLVM()) {
164        LOG_COMPILER(FATAL) << " Stub compiler is not supported for litecg ===============";
165        return false;
166    }
167    RunPipeline(static_cast<LLVMModule*>(builtinM->GetModule()), &allocator);
168
169    LOG_COMPILER(INFO) << "=============== compiling baseline stubs ===============";
170    LOptions baselineOp(optLevel_, FPFlag::RESERVE_FP, relocMode_);
171    Module* baselineM = generator.AddModule(&allocator, "baseline_stub", triple_, baselineOp, log->OutputASM(),
172                                            StubFileKind::BASELINE);
173    if (!baselineM->IsLLVM()) {
174        LOG_COMPILER(FATAL) << " Stub compiler is not supported for litecg ===============";
175        return false;
176    }
177    RunPipeline(static_cast<LLVMModule*>(baselineM->GetModule()), &allocator);
178
179    generator.SaveStubFile(filePath_);
180    return true;
181}
182
183std::string GetHelper()
184{
185    std::string str;
186    str.append(STUB_HELP_HEAD_MSG);
187    str.append(HELP_OPTION_MSG);
188    return str;
189}
190}  // namespace panda::ecmascript::kungfu
191
192int main(const int argc, const char **argv)
193{
194    panda::ecmascript::JSRuntimeOptions runtimeOptions;
195    bool ret = runtimeOptions.ParseCommand(argc, argv);
196    if (!ret) {
197        std::cerr << panda::ecmascript::kungfu::GetHelper();
198        return 1;
199    }
200
201    panda::ecmascript::Log::Initialize(runtimeOptions);
202    std::string triple = runtimeOptions.GetTargetTriple();
203    std::string stubFile = runtimeOptions.GetStubFile();
204    size_t optLevel = runtimeOptions.GetOptLevel();
205    size_t relocMode = runtimeOptions.GetRelocMode();
206    std::string logOption = runtimeOptions.GetCompilerLogOption();
207    std::string methodsList = runtimeOptions.GetMethodsListForLog();
208    bool concurrentCompile = runtimeOptions.IsConcurrentCompile();
209
210    panda::ecmascript::kungfu::CompilerLog logOpt(logOption);
211    panda::ecmascript::kungfu::MethodLogList logList(methodsList);
212    panda::ecmascript::kungfu::StubCompiler compiler(triple, stubFile, optLevel, relocMode, &logOpt, &logList,
213                                                     concurrentCompile);
214
215    bool res = compiler.BuildStubModuleAndSave();
216    LOG_COMPILER(INFO) << "stub compiler run finish, result condition(T/F):" << std::boolalpha << res;
217    return res ? 0 : -1;
218}
219