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#include "ecmascript/compiler/pass_manager.h"
16
17#include "ecmascript/compiler/bytecodes.h"
18#include "ecmascript/compiler/compilation_driver.h"
19#include "ecmascript/compiler/pass.h"
20#include "ecmascript/ecma_handle_scope.h"
21#include "ecmascript/jspandafile/js_pandafile_manager.h"
22#include "ecmascript/jspandafile/method_literal.h"
23#include "ecmascript/jspandafile/panda_file_translator.h"
24#include "ecmascript/log.h"
25#include "ecmascript/log_wrapper.h"
26#include "ecmascript/pgo_profiler/pgo_profiler.h"
27#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
28#include "ecmascript/pgo_profiler/pgo_utils.h"
29#include "ecmascript/jit/jit.h"
30#include "jsnapi_expo.h"
31
32namespace panda::ecmascript::kungfu {
33using PGOProfilerManager = pgo::PGOProfilerManager;
34
35PassContext::PassContext(const std::string &triple, CompilerLog *log, BytecodeInfoCollector* collector,
36                         IRModule *aotModule, PGOProfilerDecoder *decoder)
37    : compilationEnv_(collector->GetCompilationEnv()),
38      bcInfoCollector_(collector),
39      bytecodes_(collector->GetByteCodes()),
40      cmpCfg_(triple, &compilationEnv_->GetJSOptions()),
41      log_(log),
42      jsPandaFile_(collector->GetJSPandaFile()),
43      aotModule_(aotModule),
44      decoder_(decoder)
45{
46}
47
48
49bool JitPassManager::Compile(JSHandle<ProfileTypeInfo> &profileTypeInfo,
50                             AOTFileGenerator &gen, int32_t osrOffset)
51{
52    const JSPandaFile *jsPandaFile = compilationEnv_->GetJSPandaFile();
53    ASSERT(jsPandaFile != nullptr);
54    MethodLiteral *methodLiteral = compilationEnv_->GetMethodLiteral();
55    const uint8_t *pcStart = compilationEnv_->GetMethodPcStart();
56    const panda_file::File::Header *header = jsPandaFile->GetPandaFile()->GetHeader();
57    ApEntityId abcId = compilationEnv_->GetMethodAbcId();
58    std::string fileName(jsPandaFile->GetJSPandaFileDesc());
59
60    collector_ = new BytecodeInfoCollector(compilationEnv_, const_cast<JSPandaFile*>(jsPandaFile),
61        profilerDecoder_);
62
63    gen.SetCurrentCompileFileName(jsPandaFile->GetNormalizedFileDesc());
64    lOptions_ = new LOptions(optLevel_, FPFlag::RESERVE_FP, relocMode_);
65    cmpDriver_ = new JitCompilationDriver(profilerDecoder_,
66                                          collector_,
67                                          &gen,
68                                          fileName,
69                                          triple_,
70                                          lOptions_,
71                                          log_,
72                                          log_->OutputASM(),
73                                          maxMethodsInModule_);
74    return cmpDriver_->CompileMethod(jsPandaFile, methodLiteral, profileTypeInfo, pcStart, header, abcId,
75                                     [this, &fileName, &osrOffset] (
76                                       const CString &recordName,
77                                       const std::string &methodName,
78                                       MethodLiteral *methodLiteral,
79                                       JSHandle<ProfileTypeInfo> &profileTypeInfo,
80                                       uint32_t methodOffset,
81                                       const MethodPcInfo &methodPCInfo,
82                                       MethodInfo &methodInfo,
83                                       Module *m,
84                                       const uint8_t *pcStart,
85                                       const panda_file::File::Header *header,
86                                       ApEntityId abcId) -> bool {
87        if (compilationEnv_->GetJSOptions().GetTraceJIT()) {
88            LOG_COMPILER(INFO) << "JIT Compile Method Start: " << methodName << ", " << methodOffset << "\n";
89        }
90        ctx_ = new PassContext(triple_, log_, collector_, m->GetModule(), &profilerDecoder_);
91
92        auto jsPandaFile = ctx_->GetJSPandaFile();
93        auto cmpCfg = ctx_->GetCompilerConfig();
94        auto module = m->GetModule();
95        log_->SetMethodLog(fileName, methodName, logList_);
96
97        std::string fullName = module->GetFuncName(methodLiteral, jsPandaFile);
98        bool enableMethodLog = log_->GetEnableMethodLog();
99        if (enableMethodLog) {
100            LOG_COMPILER(INFO) << "\033[34m" << "aot method [" << fullName
101                               << "] recordName [" << recordName << "] log:" << "\033[0m";
102        }
103        Chunk chunk(compilationEnv_->GetNativeAreaAllocator());
104        if (compilationEnv_->GetJSOptions().IsEnableJITPGO()) {
105            jitProfiler_ = compilationEnv_->GetPGOProfiler()->GetJITProfile();
106            static_cast<JitCompilationEnv*>(compilationEnv_)->SetProfileTypeInfo(profileTypeInfo);
107            jitProfiler_->SetCompilationEnv(compilationEnv_);
108            jitProfiler_->InitChunk(&chunk);
109            jitProfiler_->ProfileBytecode(compilationEnv_->GetJSThread(), profileTypeInfo, nullptr,
110                                          methodLiteral->GetMethodId(), abcId, pcStart,
111                                          methodLiteral->GetCodeSize(jsPandaFile, methodLiteral->GetMethodId()),
112                                          header);
113        } else {
114            jitProfiler_ = nullptr;
115        }
116
117        if (compilationEnv_->GetJSOptions().IsEnableJitFrame()) {
118            circuit_ = new Circuit(compilationEnv_->GetNativeAreaAllocator(), ctx_->GetAOTModule()->GetDebugInfo(),
119                fullName.c_str(), cmpCfg->Is64Bit(), FrameType::FASTJIT_FUNCTION_FRAME);
120        } else {
121            circuit_ = new Circuit(compilationEnv_->GetNativeAreaAllocator(), ctx_->GetAOTModule()->GetDebugInfo(),
122                fullName.c_str(), cmpCfg->Is64Bit(), FrameType::OPTIMIZED_JS_FUNCTION_FRAME);
123        }
124
125        PGOProfilerDecoder defDecoder;
126        PGOProfilerDecoder *decoder = passOptions_->EnableOptPGOType() ? &profilerDecoder_ : &defDecoder;
127
128        builder_ = new BytecodeCircuitBuilder(jsPandaFile, methodLiteral, methodPCInfo,
129            circuit_, ctx_->GetByteCodes(), enableMethodLog && log_->OutputCIR(),
130            passOptions_->EnableTypeLowering(), fullName, recordName, decoder, false, jitProfiler_);
131        builder_->SetOsrOffset(osrOffset);
132        {
133            TimeScope timeScope("BytecodeToCircuit", methodName, methodOffset, log_);
134            builder_->SetJitCompile();
135            builder_->BytecodeToCircuit();
136            if (builder_->HasIrreducibleLoop()) {
137                LOG_JIT(DEBUG) << "compile fail as has irreducible loop:" << methodName;
138                return false;
139            }
140        }
141
142        CallMethodFlagMap methodFlagMap;
143        data_ = new PassData(builder_, circuit_, ctx_, log_, fullName, &methodInfo, recordName,
144            methodLiteral, methodOffset, &methodFlagMap, CVector<AbcFileInfo> {},
145            compilationEnv_->GetNativeAreaAllocator(), decoder, passOptions_);
146        PassRunner<PassData> pipeline(data_);
147
148        pipeline.RunPass<RunFlowCyclesVerifierPass>();
149        pipeline.RunPass<RedundantPhiEliminationPass>();
150        if (builder_->EnableLoopOptimization()) {
151            pipeline.RunPass<LoopOptimizationPass>();
152            pipeline.RunPass<RedundantPhiEliminationPass>();
153        }
154        if (passOptions_->EnableTypeLowering()) {
155            pipeline.RunPass<PGOTypeInferPass>();
156        }
157        {
158            Jit::JitLockHolder lock(compilationEnv_, "TSInlineLoweringPass");
159            pipeline.RunPass<TSInlineLoweringPass>();
160        }
161
162        pipeline.RunPass<RedundantPhiEliminationPass>();
163        pipeline.RunPass<AsyncFunctionLoweringPass>();
164        pipeline.RunPass<TypeBytecodeLoweringPass>();
165        pipeline.RunPass<UselessGateEliminationPass>();
166        pipeline.RunPass<InductionVariableAnalysisPass>();
167        pipeline.RunPass<RedundantPhiEliminationPass>();
168        pipeline.RunPass<NTypeBytecodeLoweringPass>();
169        pipeline.RunPass<UselessGateEliminationPass>();
170        pipeline.RunPass<EarlyEliminationPass>();
171        pipeline.RunPass<NumberSpeculativePass>();
172        pipeline.RunPass<UselessGateEliminationPass>();
173        pipeline.RunPass<LaterEliminationPass>();
174        if (!compilationEnv_->GetJSOptions().IsEnableJitFastCompile()) {
175            pipeline.RunPass<ValueNumberingPass>();
176        }
177        pipeline.RunPass<StateSplitLinearizerPass>();
178        pipeline.RunPass<EscapeAnalysisPass>();
179        pipeline.RunPass<StringOptimizationPass>();
180        pipeline.RunPass<NTypeHCRLoweringPass>();
181        pipeline.RunPass<TypeHCRLoweringPass>();
182        pipeline.RunPass<UselessGateEliminationPass>();
183        pipeline.RunPass<LaterEliminationPass>();
184        pipeline.RunPass<EarlyEliminationPass>();
185        pipeline.RunPass<LCRLoweringPass>();
186        pipeline.RunPass<UselessGateEliminationPass>();
187        pipeline.RunPass<ConstantFoldingPass>();
188        if (!compilationEnv_->GetJSOptions().IsEnableJitFastCompile()) {
189            pipeline.RunPass<ValueNumberingPass>();
190        }
191        pipeline.RunPass<SlowPathLoweringPass>();
192        if (!compilationEnv_->GetJSOptions().IsEnableJitFastCompile()) {
193            pipeline.RunPass<ValueNumberingPass>();
194        }
195        pipeline.RunPass<InstructionCombinePass>();
196        pipeline.RunPass<EarlyEliminationPass>();
197        pipeline.RunPass<UselessGateEliminationPass>();
198        if (!compilationEnv_->GetJSOptions().IsEnableJitFastCompile()) {
199            pipeline.RunPass<VerifierPass>();
200        }
201        pipeline.RunPass<GraphLinearizerPass>();
202        return true;
203    });
204}
205
206bool JitPassManager::RunCg()
207{
208    PassRunner<PassData> pipeline(data_);
209    pipeline.RunPass<CGIRGenPass>();
210    return cmpDriver_->RunCg();
211}
212
213JitPassManager::~JitPassManager()
214{
215    if (data_ != nullptr) {
216        delete data_;
217        data_ = nullptr;
218    }
219    if (builder_ != nullptr) {
220        delete builder_;
221        builder_ = nullptr;
222    }
223    if (circuit_ != nullptr) {
224        delete circuit_;
225        circuit_ = nullptr;
226    }
227    if (ctx_ != nullptr) {
228        delete ctx_;
229        ctx_ = nullptr;
230    }
231    if (cmpDriver_ != nullptr) {
232        delete cmpDriver_;
233        cmpDriver_ = nullptr;
234    }
235    if (lOptions_ != nullptr) {
236        delete lOptions_;
237        lOptions_ = nullptr;
238    }
239    if (collector_ != nullptr) {
240        delete collector_;
241        collector_ = nullptr;
242    }
243}
244
245void PassManager::CompileValidFiles(AOTFileGenerator &generator, bool &ret, AotCompilerStats &compilerStats)
246{
247    for (uint32_t i = 0 ; i < fileInfos_.size(); ++i) {
248        JSPandaFile *jsPandaFile = fileInfos_[i].jsPandaFile_.get();
249        auto &collector = *bcInfoCollectors_[i];
250        const std::string &extendedFilePath = fileInfos_[i].extendedFilePath_;
251        LOG_COMPILER(INFO) << "AOT compile: " << extendedFilePath;
252        generator.SetCurrentCompileFileName(jsPandaFile->GetNormalizedFileDesc());
253        if (!Compile(jsPandaFile, extendedFilePath, generator, compilerStats, collector)) {
254            ret = false;
255            continue;
256        }
257    }
258}
259
260bool PassManager::Compile(JSPandaFile *jsPandaFile, const std::string &fileName, AOTFileGenerator &gen,
261                          AotCompilerStats &compilerStats, BytecodeInfoCollector &collector)
262{
263    [[maybe_unused]] EcmaHandleScope handleScope(compilationEnv_->GetJSThread());
264
265    // Checking released/debuggable pandafile uses method literals, which are initialized in BytecodeInfoCollector,
266    // should after it.
267    if (!IsReleasedPandaFile(jsPandaFile)) {
268        LOG_COMPILER(ERROR) << "The input panda file [" << fileName
269                            << "] of AOT Compiler is debuggable version, do not use for performance test!";
270    }
271    LOptions lOptions(optLevel_, FPFlag::RESERVE_FP, relocMode_);
272    CompilationDriver cmpDriver(profilerDecoder_,
273                                &collector,
274                                &gen,
275                                fileName,
276                                triple_,
277                                &lOptions,
278                                log_,
279                                log_->OutputASM(),
280                                maxMethodsInModule_);
281
282    cmpDriver.Run(*callMethodFlagMap_, [this, &fileName, &collector](const CString recordName,
283                                                const std::string &methodName,
284                                                MethodLiteral *methodLiteral,
285                                                uint32_t methodOffset,
286                                                const MethodPcInfo &methodPCInfo,
287                                                MethodInfo &methodInfo,
288                                                Module *m) {
289        PassContext ctx(triple_, log_, &collector, m->GetModule(), &profilerDecoder_);
290        auto jsPandaFile = ctx.GetJSPandaFile();
291        auto cmpCfg = ctx.GetCompilerConfig();
292        auto module = m->GetModule();
293        log_->SetMethodLog(fileName, methodName, logList_);
294
295        std::string fullName = module->GetFuncName(methodLiteral, jsPandaFile);
296        bool enableMethodLog = log_->GetEnableMethodLog();
297        if (enableMethodLog) {
298            LOG_COMPILER(INFO) << "\033[34m" << "aot method [" << fullName
299                               << "] recordName [" << recordName << "] log:" << "\033[0m";
300        }
301
302        Circuit circuit(compilationEnv_->GetNativeAreaAllocator(), ctx.GetAOTModule()->GetDebugInfo(),
303                        fullName.c_str(), cmpCfg->Is64Bit(), FrameType::OPTIMIZED_JS_FUNCTION_FRAME);
304
305        PGOProfilerDecoder defDecoder;
306        PGOProfilerDecoder *decoder = passOptions_->EnableOptPGOType() ? &profilerDecoder_ : &defDecoder;
307
308        BytecodeCircuitBuilder builder(jsPandaFile, methodLiteral, methodPCInfo, &circuit,
309                                       ctx.GetByteCodes(), enableMethodLog && log_->OutputCIR(),
310                                       passOptions_->EnableTypeLowering(), fullName, recordName, decoder, false);
311        {
312            TimeScope timeScope("BytecodeToCircuit", methodName, methodOffset, log_);
313            builder.BytecodeToCircuit();
314        }
315
316        PassData data(&builder, &circuit, &ctx, log_, fullName, &methodInfo, recordName,
317                      methodLiteral, methodOffset, callMethodFlagMap_, fileInfos_,
318                      compilationEnv_->GetNativeAreaAllocator(), decoder, passOptions_,
319                      optBCRange_);
320        PassRunner<PassData> pipeline(&data);
321        if (!pipeline.RunPass<PreCompileCheckPass>()) {
322            return;
323        }
324        pipeline.RunPass<RunFlowCyclesVerifierPass>();
325        pipeline.RunPass<RedundantPhiEliminationPass>();
326        if (builder.EnableLoopOptimization()) {
327            pipeline.RunPass<LoopOptimizationPass>();
328            pipeline.RunPass<RedundantPhiEliminationPass>();
329        }
330        pipeline.RunPass<PGOTypeInferPass>();
331        pipeline.RunPass<TSInlineLoweringPass>();
332        pipeline.RunPass<RedundantPhiEliminationPass>();
333        pipeline.RunPass<AsyncFunctionLoweringPass>();
334        pipeline.RunPass<TypeBytecodeLoweringPass>();
335        pipeline.RunPass<UselessGateEliminationPass>();
336        pipeline.RunPass<InductionVariableAnalysisPass>();
337        pipeline.RunPass<RedundantPhiEliminationPass>();
338        pipeline.RunPass<NTypeBytecodeLoweringPass>();
339        pipeline.RunPass<UselessGateEliminationPass>();
340        pipeline.RunPass<EarlyEliminationPass>();
341        pipeline.RunPass<NumberSpeculativePass>();
342        pipeline.RunPass<UselessGateEliminationPass>();
343        pipeline.RunPass<LaterEliminationPass>();
344        pipeline.RunPass<ValueNumberingPass>();
345        pipeline.RunPass<StateSplitLinearizerPass>();
346        pipeline.RunPass<EscapeAnalysisPass>();
347        pipeline.RunPass<StringOptimizationPass>();
348        pipeline.RunPass<NTypeHCRLoweringPass>();
349        pipeline.RunPass<TypeHCRLoweringPass>();
350        pipeline.RunPass<UselessGateEliminationPass>();
351        pipeline.RunPass<LaterEliminationPass>();
352        pipeline.RunPass<EarlyEliminationPass>();
353        pipeline.RunPass<LCRLoweringPass>();
354        pipeline.RunPass<UselessGateEliminationPass>();
355        pipeline.RunPass<ConstantFoldingPass>();
356        pipeline.RunPass<ValueNumberingPass>();
357        pipeline.RunPass<SlowPathLoweringPass>();
358        pipeline.RunPass<ValueNumberingPass>();
359        pipeline.RunPass<InstructionCombinePass>();
360        pipeline.RunPass<EarlyEliminationPass>();
361        pipeline.RunPass<UselessGateEliminationPass>();
362        if (passOptions_->EnableVerifierPass()) {
363            pipeline.RunPass<VerifierPass>();
364        }
365        pipeline.RunPass<GraphLinearizerPass>();
366        pipeline.RunPass<CGIRGenPass>();
367    });
368
369    compilerStats.SetCompilerMethodCount(cmpDriver.GetCompilerMethodCount());
370    LOG_COMPILER(INFO) << collector.GetBytecodeInfo().GetSkippedMethodSize()
371                       << " methods have been skipped";
372    return true;
373}
374
375bool PassManager::IsReleasedPandaFile(const JSPandaFile *jsPandaFile) const
376{
377    MethodLiteral* methodLiteral = jsPandaFile->GetMethodLiterals();
378    if (methodLiteral == nullptr) {
379        LOG_COMPILER(ERROR) << "There is no mehtod literal in " << jsPandaFile->GetJSPandaFileDesc();
380        return false;
381    }
382
383    panda_file::File::EntityId methodId = methodLiteral->GetMethodId();
384    ASSERT(methodId.IsValid());
385    DebugInfoExtractor *debugInfoExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile);
386    LocalVariableTable lvt = debugInfoExtractor->GetLocalVariableTable(methodId);
387    return lvt.empty();
388}
389} // namespace panda::ecmascript::kungfu
390