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