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
53 if (mergeAbc_) {
54 // generate merged abc
55 auto emitMergedAbcJob = new EmitMergedAbcJob(options_, progsInfo_);
56 // One job should be placed before the jobs on which it depends to prevent blocking
57 jobs_.push_back(emitMergedAbcJob);
58 jobsCount_++;
59 // Disable generating cached files when cross-program optimization is required, to prevent cached files from
60 // not being invalidated when their dependencies are changed
61 if (!options_->CompilerOptions().requireGlobalOptimization) {
62 ScheduleEmitCacheJobs(emitMergedAbcJob);
63 }
64 } else {
65 auto targetApi = options_->CompilerOptions().targetApiVersion;
66 auto targetSubApi = options_->CompilerOptions().targetApiSubVersion;
67 for (const auto &info: progsInfo_) {
68 try {
69 // generate multi abcs
70 auto outputFileName = options_->OutputFiles().empty() ? options_->CompilerOutput() :
71 options_->OutputFiles().at(info.first);
72 auto emitSingleAbcJob = new EmitSingleAbcJob(outputFileName, &(info.second->program), statp_,
73 targetApi, targetSubApi);
74 jobs_.push_back(emitSingleAbcJob);
75 jobsCount_++;
76 } catch (std::exception &error) {
77 throw Error(ErrorType::GENERIC, error.what());
78 }
79 }
80 }
81
82 lock.unlock();
83 jobsAvailable_.notify_all();
84 }
85
Run()86 void EmitSingleAbcJob::Run()
87 {
88 panda::Timer::timerStart(panda::EVENT_EMIT_SINGLE_PROGRAM, outputFileName_);
89 if (!panda::pandasm::AsmEmitter::Emit(panda::os::file::File::GetExtendedFilePath(outputFileName_), *prog_, statp_,
90 nullptr, true, nullptr, targetApiVersion_, targetApiSubVersion_)) {
91 throw Error(ErrorType::GENERIC, "Failed to emit " + outputFileName_ + ", error: " +
92 panda::pandasm::AsmEmitter::GetLastError());
93 }
94 for (auto *dependant : dependants_) {
95 dependant->Signal();
96 }
97 panda::Timer::timerEnd(panda::EVENT_EMIT_SINGLE_PROGRAM, outputFileName_);
98 }
99
Run()100 void EmitMergedAbcJob::Run()
101 {
102 std::unique_lock<std::mutex> lock(m_);
103 cond_.wait(lock, [this] { return dependencies_ == 0; });
104 panda::Timer::timerStart(panda::EVENT_EMIT_MERGED_PROGRAM, "");
105 std::vector<panda::pandasm::Program*> progs;
106 progs.reserve(progsInfo_.size());
107 for (const auto &info: progsInfo_) {
108 progs.push_back(&(info.second->program));
109 }
110
111 panda::pandasm::EmitterConfig emitConfig {targetApiVersion_, targetApiSubVersion_,
112 options_->CompilerOptions().isDebug, options_->CompilerOptions().fileThreadCount};
113 bool success = panda::pandasm::AsmEmitter::EmitPrograms(
114 panda::os::file::File::GetExtendedFilePath(outputFileName_), progs, true,
115 emitConfig);
116
117 panda::Timer::timerEnd(panda::EVENT_EMIT_MERGED_PROGRAM, "");
118
119 if (!success) {
120 throw Error(ErrorType::GENERIC, "Failed to emit " + outputFileName_ + ", error: " +
121 panda::pandasm::AsmEmitter::GetLastError() +
122 "\nIf you're using any cache file generated by older version of SDK, " +
123 "please try cleaning the cache files and rebuild");
124 }
125
126 if (!transformLib_.empty()) {
127 util::Helpers::AopTransform(outputFileName_, transformLib_);
128 }
129 }
130
Run()131 void EmitCacheJob::Run()
132 {
133 panda::Timer::timerStart(panda::EVENT_EMIT_CACHE_FILE, outputProtoName_);
134 panda::proto::ProtobufSnapshotGenerator::UpdateCacheFile(progCache_, outputProtoName_);
135 for (auto *dependant : dependants_) {
136 dependant->Signal();
137 }
138 panda::Timer::timerEnd(panda::EVENT_EMIT_CACHE_FILE, outputProtoName_);
139 }
140
141 } // namespace panda::es2panda::util
142