1/*
2 * Copyright (c) 2023 - 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 "emitFiles.h"
17
18#include <assembly-emitter.h>
19#include <mem/arena_allocator.h>
20#include "utils/timers.h"
21
22#include <es2panda.h>
23#include <protobufSnapshotGenerator.h>
24#include <util/helpers.h>
25
26namespace panda::es2panda::aot {
27void EmitFileQueue::ScheduleEmitCacheJobs(EmitMergedAbcJob *emitMergedAbcJob)
28{
29    for (const auto &info: progsInfo_) {
30        // generate cache protoBins and set dependencies
31        if (!info.second->needUpdateCache) {
32            continue;
33        }
34        auto outputCacheIter = options_->CompilerOptions().cacheFiles.find(info.first);
35        if (outputCacheIter != options_->CompilerOptions().cacheFiles.end()) {
36            auto emitProtoJob = new EmitCacheJob(outputCacheIter->second, info.second);
37            emitProtoJob->DependsOn(emitMergedAbcJob);
38            jobs_.push_back(emitProtoJob);
39            jobsCount_++;
40        }
41    }
42}
43
44void EmitFileQueue::Schedule()
45{
46    ASSERT(jobsCount_ == 0);
47    std::unique_lock<std::mutex> lock(m_);
48    auto targetApi = options_->CompilerOptions().targetApiVersion;
49    auto targetSubApi = options_->CompilerOptions().targetApiSubVersion;
50
51    if (mergeAbc_) {
52        // generate merged abc
53        auto emitMergedAbcJob = new EmitMergedAbcJob(options_->CompilerOutput(),
54            options_->CompilerOptions().transformLib, progsInfo_, targetApi, targetSubApi);
55        // Disable generating cached files when cross-program optimization is required, to prevent cached files from
56        // not being invalidated when their dependencies are changed
57        if (!options_->CompilerOptions().requireGlobalOptimization) {
58            ScheduleEmitCacheJobs(emitMergedAbcJob);
59        }
60        //  One job should be placed after those jobs which depend on it to prevent blocking
61        jobs_.push_back(emitMergedAbcJob);
62        jobsCount_++;
63    } else {
64        for (const auto &info: progsInfo_) {
65            try {
66                // generate multi abcs
67                auto outputFileName = options_->OutputFiles().empty() ? options_->CompilerOutput() :
68                    options_->OutputFiles().at(info.first);
69                auto emitSingleAbcJob = new EmitSingleAbcJob(outputFileName, &(info.second->program), statp_,
70                                                             targetApi, targetSubApi);
71                jobs_.push_back(emitSingleAbcJob);
72                jobsCount_++;
73            } catch (std::exception &error) {
74                throw Error(ErrorType::GENERIC, error.what());
75            }
76        }
77    }
78
79    lock.unlock();
80    jobsAvailable_.notify_all();
81}
82
83void EmitSingleAbcJob::Run()
84{
85    panda::Timer::timerStart(panda::EVENT_EMIT_SINGLE_PROGRAM, outputFileName_);
86    if (!panda::pandasm::AsmEmitter::Emit(panda::os::file::File::GetExtendedFilePath(outputFileName_), *prog_, statp_,
87        nullptr, true, nullptr, targetApiVersion_, targetApiSubVersion_)) {
88        throw Error(ErrorType::GENERIC, "Failed to emit " + outputFileName_ + ", error: " +
89            panda::pandasm::AsmEmitter::GetLastError());
90    }
91    for (auto *dependant : dependants_) {
92        dependant->Signal();
93    }
94    panda::Timer::timerEnd(panda::EVENT_EMIT_SINGLE_PROGRAM, outputFileName_);
95}
96
97void EmitMergedAbcJob::Run()
98{
99    panda::Timer::timerStart(panda::EVENT_EMIT_MERGED_PROGRAM, "");
100    std::vector<panda::pandasm::Program*> progs;
101    progs.reserve(progsInfo_.size());
102    for (const auto &info: progsInfo_) {
103        progs.push_back(&(info.second->program));
104    }
105
106    bool success = panda::pandasm::AsmEmitter::EmitPrograms(
107        panda::os::file::File::GetExtendedFilePath(outputFileName_), progs, true,
108        targetApiVersion_, targetApiSubVersion_);
109
110    for (auto *dependant : dependants_) {
111        dependant->Signal();
112    }
113    panda::Timer::timerEnd(panda::EVENT_EMIT_MERGED_PROGRAM, "");
114
115    if (!success) {
116        throw Error(ErrorType::GENERIC, "Failed to emit " + outputFileName_ + ", error: " +
117            panda::pandasm::AsmEmitter::GetLastError() +
118            "\nIf you're using any cache file generated by older version of SDK, " +
119            "please try cleaning the cache files and rebuild");
120    }
121
122    if (!transformLib_.empty()) {
123        util::Helpers::AopTransform(outputFileName_, transformLib_);
124    }
125}
126
127void EmitCacheJob::Run()
128{
129    std::unique_lock<std::mutex> lock(m_);
130    cond_.wait(lock, [this] { return dependencies_ == 0; });
131    panda::Timer::timerStart(panda::EVENT_EMIT_CACHE_FILE, outputProtoName_);
132    panda::proto::ProtobufSnapshotGenerator::UpdateCacheFile(progCache_, outputProtoName_);
133    panda::Timer::timerEnd(panda::EVENT_EMIT_CACHE_FILE, outputProtoName_);
134}
135
136}  // namespace panda::es2panda::util
137