• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 "compileQueue.h"
17 
18 #include "utils/timers.h"
19 
20 #include <compiler/core/compilerContext.h>
21 #include <compiler/core/emitter/emitter.h>
22 #include <compiler/core/function.h>
23 #include <compiler/core/pandagen.h>
24 #include <protobufSnapshotGenerator.h>
25 #include <util/commonUtil.h>
26 
27 namespace panda::es2panda::compiler {
28 
29 std::mutex CompileFileJob::globalMutex_;
30 std::mutex CompileAbcClassQueue::globalMutex_;
31 
Run()32 void CompileFunctionJob::Run()
33 {
34     std::unique_lock<std::mutex> lock(m_);
35     cond_.wait(lock, [this] { return dependencies_ == 0; });
36 
37     ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true);
38     PandaGen pg(&allocator, context_, scope_);
39 
40     Function::Compile(&pg);
41 
42     FunctionEmitter funcEmitter(&allocator, &pg);
43     funcEmitter.Generate(context_->PatchFixHelper());
44 
45     context_->GetEmitter()->AddFunction(&funcEmitter, context_);
46 
47     for (auto *dependant : dependants_) {
48         dependant->Signal();
49     }
50 }
51 
Run()52 void CompileModuleRecordJob::Run()
53 {
54     std::unique_lock<std::mutex> lock(m_);
55     cond_.wait(lock, [this] { return dependencies_ == 0; });
56 
57     bool hasLazyImport = context_->Binder()->Program()->ModuleRecord()->HasLazyImport();
58     ModuleRecordEmitter moduleEmitter(context_->Binder()->Program()->ModuleRecord(), context_->NewLiteralIndex(),
59         hasLazyImport ? context_->NewLiteralIndex() : -1);
60     moduleEmitter.Generate();
61 
62     context_->GetEmitter()->AddSourceTextModuleRecord(&moduleEmitter, context_);
63 
64     for (auto *dependant : dependants_) {
65         dependant->Signal();
66     }
67 }
68 
RetrieveProgramFromCacheFiles(const std::string & buffer,bool isAbcFile)69 bool CompileFileJob::RetrieveProgramFromCacheFiles(const std::string &buffer, bool isAbcFile)
70 {
71     if (options_->requireGlobalOptimization) {
72         return false;
73     }
74     auto cacheFileIter = options_->cacheFiles.find(src_->fileName);
75     // Disable the use of file caching when cross-program optimization is required, to prevent cached files from
76     // not being invalidated when their dependencies change, or from not being reanalyzed when their dependents
77     // are updated
78     if (cacheFileIter != options_->cacheFiles.end()) {
79         // cache is invalid when any one of source file infos being changed
80         auto bufToHash = buffer + src_->fileName + src_->recordName + src_->sourcefile + src_->pkgName;
81         ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true);
82         if (!isAbcFile) {
83             src_->hash = GetHash32String(reinterpret_cast<const uint8_t *>(bufToHash.c_str()));
84             auto *cacheProgramInfo = proto::ProtobufSnapshotGenerator::GetCacheContext(cacheFileIter->second,
85                                                                                        &allocator);
86             if (cacheProgramInfo != nullptr && cacheProgramInfo->hashCode == src_->hash) {
87                 std::unique_lock<std::mutex> lock(globalMutex_);
88                 auto *cache = allocator_->New<util::ProgramCache>(src_->hash, std::move(cacheProgramInfo->program));
89                 progsInfo_.insert({src_->fileName, cache});
90                 return true;
91             }
92         } else {
93             std::map<std::string, PkgInfo> updateVersionInfo =
94                 options_->compileContextInfo.updateVersionInfo[src_->pkgName];
95             auto abcBufToHash = bufToHash;
96             for (auto &[pkgName, pkgInfo]: updateVersionInfo) {
97                 /**
98                  * When the bytecode har dependency package version changes, it needs to be recompiled, so the hash
99                  * value needs to change
100                  */
101                 abcBufToHash = abcBufToHash + pkgName + ":" + pkgInfo.version;
102             }
103             // An ABC file starts with '\0', but the 'GetHash32String' method does not support this format.
104             src_->hash = GetHash32(reinterpret_cast<const uint8_t *>(abcBufToHash.c_str()), abcBufToHash.size());
105             auto *cacheAbcProgramsInfo = proto::ProtobufSnapshotGenerator::GetAbcInputCacheContext(
106                 cacheFileIter->second, &allocator);
107             if (cacheAbcProgramsInfo != nullptr && cacheAbcProgramsInfo->hashCode == src_->hash) {
108                 InsertAbcCachePrograms(src_->hash, cacheAbcProgramsInfo->programsCache);
109                 delete cacheAbcProgramsInfo;
110                 cacheAbcProgramsInfo = nullptr;
111                 return true;
112             }
113             delete cacheAbcProgramsInfo;
114             cacheAbcProgramsInfo = nullptr;
115         }
116     }
117     return false;
118 }
119 
InsertAbcCachePrograms(uint32_t hashCode,std::map<std::string,panda::es2panda::util::ProgramCache * > & abcProgramsInfo)120 void CompileFileJob::InsertAbcCachePrograms(uint32_t hashCode,
121     std::map<std::string, panda::es2panda::util::ProgramCache *> &abcProgramsInfo)
122 {
123     std::unique_lock<std::mutex> lock(globalMutex_);
124     Compiler::SetExpectedProgsCount(Compiler::GetExpectedProgsCount() + abcProgramsInfo.size() - 1);
125     for (auto pair : abcProgramsInfo) {
126         ASSERT(progsInfo_.find(pair.first) == progsInfo_.end());
127         pair.second->program.isGeneratedFromMergedAbc = true;
128         auto *cache = allocator_->New<util::ProgramCache>(hashCode, std::move(pair.second->program), false);
129         progsInfo_.insert({pair.first, cache});
130     }
131 }
132 
Run()133 void CompileFileJob::Run()
134 {
135     std::stringstream ss;
136     std::string buffer;
137     panda::Timer::timerStart(panda::EVENT_READ_INPUT_AND_CACHE, src_->fileName);
138     if (!src_->fileName.empty()) {
139         if (!util::Helpers::ReadFileToBuffer(src_->fileName, ss)) {
140             return;
141         }
142         buffer = ss.str();
143         src_->source = buffer;
144         if (RetrieveProgramFromCacheFiles(buffer, !src_->isSourceMode)) {
145             panda::Timer::timerEnd(panda::EVENT_READ_INPUT_AND_CACHE, src_->fileName);
146             return;
147         }
148     }
149     panda::Timer::timerEnd(panda::EVENT_READ_INPUT_AND_CACHE, src_->fileName);
150 
151     CompileProgram();
152 }
153 
CompileProgram()154 void CompileFileJob::CompileProgram()
155 {
156     es2panda::Compiler compiler(src_->scriptExtension, options_->functionThreadCount);
157     panda::pandasm::Program *prog = nullptr;
158 
159     if (src_->isSourceMode) {
160         panda::Timer::timerStart(panda::EVENT_COMPILE_FILE, src_->fileName);
161         prog = compiler.CompileFile(*options_, src_, symbolTable_);
162         panda::Timer::timerEnd(panda::EVENT_COMPILE_FILE, src_->fileName);
163     } else if (!options_->mergeAbc) {
164         // If input is an abc file, in non merge-abc mode, compile classes one by one.
165         panda::Timer::timerStart(panda::EVENT_COMPILE_ABC_FILE, src_->fileName);
166         prog = compiler.CompileAbcFile(src_->fileName, *options_);
167         panda::Timer::timerEnd(panda::EVENT_COMPILE_ABC_FILE, src_->fileName);
168     } else {
169         // If input is an abc file, in merge-abc mode, compile each class parallelly.
170         CompileAbcFileJobInParallel(compiler);
171         return;
172     }
173 
174     if (prog == nullptr) {
175         return;
176     }
177 
178     OptimizeAndCacheProgram(prog);
179 }
180 
CompileAbcFileJobInParallel(es2panda::Compiler & compiler)181 void CompileFileJob::CompileAbcFileJobInParallel(es2panda::Compiler &compiler)
182 {
183     std::map<std::string, panda::es2panda::util::ProgramCache *> abcProgramsInfo {};
184 
185     panda::Timer::timerStart(panda::EVENT_COMPILE_ABC_FILE, src_->fileName);
186     compiler.CompileAbcFileInParallel(src_, *options_, abcProgramsInfo, allocator_);
187     panda::Timer::timerEnd(panda::EVENT_COMPILE_ABC_FILE, src_->fileName);
188 
189     panda::Timer::timerStart(panda::EVENT_UPDATE_ABC_PROG_CACHE, src_->fileName);
190     auto outputCacheIter = options_->cacheFiles.find(src_->fileName);
191     if (!options_->requireGlobalOptimization && outputCacheIter != options_->cacheFiles.end()) {
192         auto *cache = new panda::es2panda::util::AbcProgramsCache(src_->hash, abcProgramsInfo);
193         CHECK_NOT_NULL(cache);
194         panda::proto::ProtobufSnapshotGenerator::UpdateAbcCacheFile(cache, outputCacheIter->second);
195         delete cache;
196         cache = nullptr;
197     }
198     InsertAbcCachePrograms(src_->hash, abcProgramsInfo);
199     panda::Timer::timerEnd(panda::EVENT_UPDATE_ABC_PROG_CACHE, src_->fileName);
200 }
201 
OptimizeAndCacheProgram(panda::pandasm::Program * prog)202 void CompileFileJob::OptimizeAndCacheProgram(panda::pandasm::Program *prog)
203 {
204     bool requireOptimizationAfterAnalysis = false;
205     // When cross-program optimizations are required, skip program-local optimization at this stage
206     // and perform it later after the analysis of all programs has been completed
207     if (src_->isSourceMode && options_->transformLib.empty()) {
208         if (options_->requireGlobalOptimization) {
209             panda::Timer::timerStart(panda::EVENT_OPTIMIZE_PROGRAM, src_->fileName);
210             util::Helpers::AnalysisProgram(prog, src_->fileName);
211             requireOptimizationAfterAnalysis = true;
212         } else if (options_->optLevel != 0) {
213             panda::Timer::timerStart(panda::EVENT_OPTIMIZE_PROGRAM, src_->fileName);
214             util::Helpers::OptimizeProgram(prog, src_->fileName);
215             panda::Timer::timerEnd(panda::EVENT_OPTIMIZE_PROGRAM, src_->fileName);
216         }
217     }
218 
219     {
220         std::unique_lock<std::mutex> lock(globalMutex_);
221         auto *cache = allocator_->New<util::ProgramCache>(src_->hash, std::move(*prog), src_->isSourceMode);
222         progsInfo_.insert({src_->fileName, cache});
223         if (requireOptimizationAfterAnalysis) {
224             optimizationPendingProgs_.insert(src_->fileName);
225         }
226     }
227 }
228 
Run()229 void CompileAbcClassJob::Run()
230 {
231     panda_file::File::EntityId recordId(classId_);
232     auto *program = new panda::pandasm::Program();
233     std::string record_name = "";
234     compiler_.CompileAbcClass(recordId, *program, record_name);
235     if (program->record_table.size() == 0) {
236         delete program;
237         return;
238     }
239     program->isGeneratedFromMergedAbc = true;
240 
241     if (!options_.modifiedPkgName.empty()) {
242         UpdatePkgNameOfImportOhmurl(program, options_);
243     }
244     // Update ohmurl for abc input when needed
245     if (options_.compileContextInfo.needModifyRecord ||
246         (options_.updatePkgVersionForAbcInput && pkgVersionUpdateRequiredInAbc_)) {
247         panda::Timer::timerStart(panda::EVENT_UPDATE_ABC_PKG_VERSION, record_name);
248         UpdateImportOhmurl(program, options_);
249         panda::Timer::timerEnd(panda::EVENT_UPDATE_ABC_PKG_VERSION, record_name);
250         // Remove redundant strings created due to version replacement
251         panda::Timer::timerStart(panda::EVENT_UPDATE_ABC_PROGRAM_STRING, record_name);
252         if (options_.removeRedundantFile && hasOhmurlBeenChanged_) {
253             program->strings.clear();
254             for (const auto &[_, function] : program->function_table) {
255                 const auto &funcStringSet = function.CollectStringsFromFunctionInsns();
256                 program->strings.insert(funcStringSet.begin(), funcStringSet.end());
257             }
258         }
259         panda::Timer::timerEnd(panda::EVENT_UPDATE_ABC_PROGRAM_STRING, record_name);
260     }
261 
262     panda::Timer::timerStart(panda::EVENT_UPDATE_ABC_PROG_CACHE, record_name);
263     {
264         std::unique_lock<std::mutex> lock(CompileFileJob::globalMutex_);
265         ASSERT(compiler_.GetAbcFile().GetFilename().find(util::CHAR_VERTICAL_LINE) == std::string::npos);
266         ASSERT(program->record_table.size() == 1);
267         ASSERT(util::RecordNotGeneratedFromBytecode(program->record_table.begin()->first));
268         auto name = compiler_.GetAbcFile().GetFilename();
269         name += util::CHAR_VERTICAL_LINE + program->record_table.begin()->first;
270         auto *cache = allocator_->New<util::ProgramCache>(src_->hash, std::move(*program), true);
271         ASSERT(!progsInfo_.count(name));
272         progsInfo_.emplace(name, cache);
273     }
274     panda::Timer::timerEnd(panda::EVENT_UPDATE_ABC_PROG_CACHE, record_name);
275 
276     delete program;
277     program = nullptr;
278 }
279 
UpdateBundleNameOfOhmurl(std::string & ohmurl)280 void CompileAbcClassJob::UpdateBundleNameOfOhmurl(std::string &ohmurl)
281 {
282     const auto &newOhmurl = util::UpdateBundleNameIfNeeded(ohmurl, options_.compileContextInfo.bundleName,
283                                                            options_.compileContextInfo.externalPkgNames);
284     if (newOhmurl == ohmurl) {
285         return;
286     }
287     SetOhmurlBeenChanged(true);
288     ohmurl = newOhmurl;
289 }
290 
UpdateDynamicImport(panda::pandasm::Program * prog,const std::map<std::string,panda::es2panda::PkgInfo> & pkgContextInfo)291 void CompileAbcClassJob::UpdateDynamicImport(panda::pandasm::Program *prog,
292     const std::map<std::string, panda::es2panda::PkgInfo> &pkgContextInfo)
293 {
294     for (auto &[name, function] : prog->function_table) {
295         util::VisitDyanmicImports<false>(function, [this, &prog, pkgContextInfo](std::string &ohmurl) {
296             if (this->options_.compileContextInfo.needModifyRecord) {
297                 this->UpdateBundleNameOfOhmurl(ohmurl);
298             }
299             const auto &newOhmurl = util::UpdatePackageVersionIfNeeded(ohmurl, pkgContextInfo);
300             if (newOhmurl == ohmurl) {
301                 return;
302             }
303             prog->strings.insert(newOhmurl);
304             this->SetOhmurlBeenChanged(true);
305             ohmurl = newOhmurl;
306         });
307     }
308 }
309 
UpdateStaticImport(panda::pandasm::Program * prog,const std::map<std::string,panda::es2panda::PkgInfo> & pkgContextInfo)310 void CompileAbcClassJob::UpdateStaticImport(panda::pandasm::Program *prog,
311     const std::map<std::string, panda::es2panda::PkgInfo> &pkgContextInfo)
312 {
313     for (auto &[recordName, record] : prog->record_table) {
314         util::VisitStaticImports<false>(*prog, record, [this, pkgContextInfo](std::string &ohmurl) {
315             if (this->options_.compileContextInfo.needModifyRecord) {
316                 this->UpdateBundleNameOfOhmurl(ohmurl);
317             }
318 
319             const auto &newOhmurl = util::UpdatePackageVersionIfNeeded(ohmurl, pkgContextInfo);
320             if (newOhmurl == ohmurl) {
321                 return;
322             }
323             this->SetOhmurlBeenChanged(true);
324             ohmurl = newOhmurl;
325         });
326     }
327 }
328 
UpdateImportOhmurl(panda::pandasm::Program * prog,const panda::es2panda::CompilerOptions & options)329 void CompileAbcClassJob::UpdateImportOhmurl(panda::pandasm::Program *prog,
330                                             const panda::es2panda::CompilerOptions &options)
331 {
332     bool isAccurateUpdateVersion = !options.compileContextInfo.updateVersionInfo.empty();
333     const std::map<std::string, panda::es2panda::PkgInfo> &pkgContextInfo = isAccurateUpdateVersion ?
334         options.compileContextInfo.updateVersionInfo.at(abcPkgName_) : options.compileContextInfo.pkgContextInfo;
335     // Replace for esm module static import
336     UpdateStaticImport(prog, pkgContextInfo);
337     // Replace for dynamic import
338     UpdateDynamicImport(prog, pkgContextInfo);
339 }
340 /**
341  * Need to modify the package name of the original package to the package name of the target package when
342  * you merging two packages.
343  */
UpdatePkgNameOfImportOhmurl(panda::pandasm::Program * prog,const panda::es2panda::CompilerOptions & options)344 void CompileAbcClassJob::UpdatePkgNameOfImportOhmurl(panda::pandasm::Program *prog,
345     const panda::es2panda::CompilerOptions &options)
346 {
347     for (auto &[recordName, record] : prog->record_table) {
348         util::VisitStaticImports<false>(*prog, record, [this, options](std::string &ohmurl) {
349             const auto &newOhmurl = util::UpdatePackageNameIfNeeded(ohmurl, options.modifiedPkgName);
350             if (newOhmurl == ohmurl) {
351                 return;
352             }
353             this->SetOhmurlBeenChanged(true);
354             ohmurl = newOhmurl;
355         });
356     }
357     for (auto &[name, function] : prog->function_table) {
358         util::VisitDyanmicImports<false>(function, [this, options](std::string &ohmurl) {
359             const auto &newOhmurl = util::UpdatePackageNameIfNeeded(ohmurl, options.modifiedPkgName);
360             if (newOhmurl == ohmurl) {
361                 return;
362             }
363             this->SetOhmurlBeenChanged(true);
364             ohmurl = newOhmurl;
365         });
366     }
367     if (hasOhmurlBeenChanged_) {
368         prog->strings.clear();
369         for (const auto &[_, function] : prog->function_table) {
370             const auto &funcStringSet = function.CollectStringsFromFunctionInsns();
371             prog->strings.insert(funcStringSet.begin(), funcStringSet.end());
372         }
373     }
374 }
375 
Run()376 void PostAnalysisOptimizeFileJob::Run()
377 {
378     util::Helpers::OptimizeProgram(program_, fileName_);
379     panda::Timer::timerEnd(panda::EVENT_OPTIMIZE_PROGRAM, fileName_);
380 }
381 
Schedule()382 void CompileFuncQueue::Schedule()
383 {
384     ASSERT(jobsCount_ == 0);
385     std::unique_lock<std::mutex> lock(m_);
386     const auto &functions = context_->Binder()->Functions();
387 
388     for (auto *function : functions) {
389         auto *funcJob = new CompileFunctionJob(context_);
390         funcJob->SetFunctionScope(function);
391         jobs_.push_back(funcJob);
392         jobsCount_++;
393     }
394 
395     if (context_->Binder()->Program()->Kind() == parser::ScriptKind::MODULE) {
396         auto *moduleRecordJob = new CompileModuleRecordJob(context_);
397         jobs_.push_back(moduleRecordJob);
398         jobsCount_++;
399     }
400 
401     lock.unlock();
402     jobsAvailable_.notify_all();
403 }
404 
Schedule()405 void CompileFileQueue::Schedule()
406 {
407     ASSERT(jobsCount_ == 0);
408     std::unique_lock<std::mutex> lock(m_);
409 
410     for (auto &input: options_->sourceFiles) {
411         auto *fileJob = new CompileFileJob(&input, options_, progsInfo_, optimizationPendingProgs_,
412                                            symbolTable_, allocator_);
413         jobs_.push_back(fileJob);
414         jobsCount_++;
415     }
416 
417     lock.unlock();
418     jobsAvailable_.notify_all();
419 }
420 
NeedUpdateVersion()421 bool CompileAbcClassQueue::NeedUpdateVersion()
422 {
423     std::unordered_map<std::string, std::map<std::string, panda::es2panda::PkgInfo>> updateVersionInfo =
424         options_.compileContextInfo.updateVersionInfo;
425     auto iter = updateVersionInfo.find(src_->pkgName);
426     return updateVersionInfo.empty() || (iter != updateVersionInfo.end() && !iter->second.empty());
427 }
428 
Schedule()429 void CompileAbcClassQueue::Schedule()
430 {
431     std::unique_lock<std::mutex> lock(m_);
432 
433     auto classIds = compiler_.GetAbcFile().GetClasses();
434     bool needUpdateVersion = NeedUpdateVersion();
435     for (size_t i = 0; i != classIds.size(); ++i) {
436         if (!compiler_.CheckClassId(classIds[i], i)) {
437             continue;
438         }
439 
440         auto *abcClassJob = new CompileAbcClassJob(src_, classIds[i], options_, compiler_, progsInfo_, allocator_,
441                                                    src_->pkgName, needUpdateVersion);
442 
443         jobs_.push_back(abcClassJob);
444         jobsCount_++;
445     }
446 
447     lock.unlock();
448     jobsAvailable_.notify_all();
449 }
450 
Schedule()451 void PostAnalysisOptimizeFileQueue::Schedule()
452 {
453     ASSERT(jobsCount_ == 0);
454     std::unique_lock<std::mutex> lock(m_);
455 
456     for (const auto &optimizationPendingProgName : optimizationPendingProgs_) {
457         auto progInfo = progsInfo_.find(optimizationPendingProgName);
458         if (progInfo == progsInfo_.end()) {
459             continue;
460         }
461         auto *optimizeJob = new PostAnalysisOptimizeFileJob(progInfo->first, &progInfo->second->program);
462         jobs_.push_back(optimizeJob);
463         jobsCount_++;
464     }
465 }
466 
467 }  // namespace panda::es2panda::compiler
468