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 
26 namespace panda::es2panda::aot {
ScheduleEmitCacheJobs(EmitMergedAbcJob *emitMergedAbcJob)27 void 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 
Schedule()44 void 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 
Run()83 void 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 
Run()97 void 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 
Run()127 void 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