1 /*
2 * Copyright (c) 2023 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 #include "ecmascript/compiler/aot_compiler_preprocessor.h"
16 #include "ecmascript/compiler/pgo_type/pgo_type_parser.h"
17 #include "ecmascript/jspandafile/program_object.h"
18 #include "ecmascript/module/js_module_manager.h"
19 #include "ecmascript/ohos/ohos_pgo_processor.h"
20 #include "ecmascript/ohos/ohos_pkg_args.h"
21
22 namespace panda::ecmascript::kungfu {
23 namespace {
24 constexpr int32_t DEFAULT_OPT_LEVEL = 3; // 3: default opt level
25 } // namespace
26 using PGOProfilerManager = pgo::PGOProfilerManager;
27
CompilationOptions(EcmaVM * vm,JSRuntimeOptions & runtimeOptions)28 CompilationOptions::CompilationOptions(EcmaVM *vm, JSRuntimeOptions &runtimeOptions)
29 {
30 triple_ = runtimeOptions.GetTargetTriple();
31 if (runtimeOptions.GetAOTOutputFile().empty()) {
32 runtimeOptions.SetAOTOutputFile("aot_file");
33 }
34 outputFileName_ = runtimeOptions.GetAOTOutputFile();
35 optLevel_ = runtimeOptions.GetOptLevel();
36 relocMode_ = runtimeOptions.GetRelocMode();
37 logOption_ = runtimeOptions.GetCompilerLogOption();
38 logMethodsList_ = runtimeOptions.GetMethodsListForLog();
39 compilerLogTime_ = runtimeOptions.IsEnableCompilerLogTime();
40 maxAotMethodSize_ = runtimeOptions.GetMaxAotMethodSize();
41 maxMethodsInModule_ = runtimeOptions.GetCompilerModuleMethods();
42 hotnessThreshold_ = runtimeOptions.GetPGOHotnessThreshold();
43 profilerIn_ = std::string(runtimeOptions.GetPGOProfilerPath());
44 needMerge_ = false;
45 isEnableArrayBoundsCheckElimination_ = runtimeOptions.IsEnableArrayBoundsCheckElimination();
46 isEnableTypeLowering_ = runtimeOptions.IsEnableTypeLowering();
47 isEnableEarlyElimination_ = runtimeOptions.IsEnableEarlyElimination();
48 isEnableLaterElimination_ = runtimeOptions.IsEnableLaterElimination();
49 isEnableValueNumbering_ = runtimeOptions.IsEnableValueNumbering();
50 isEnableOptInlining_ = runtimeOptions.IsEnableOptInlining();
51 isEnableOptString_ = runtimeOptions.IsEnableOptString();
52 isEnableTypeInfer_ = isEnableTypeLowering_ ||
53 vm->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->AssertTypes();
54 isEnableOptPGOType_ = runtimeOptions.IsEnableOptPGOType();
55 isEnableOptTrackField_ = runtimeOptions.IsEnableOptTrackField();
56 isEnableOptLoopPeeling_ = runtimeOptions.IsEnableOptLoopPeeling();
57 isEnableOptLoopInvariantCodeMotion_ = runtimeOptions.IsEnableOptLoopInvariantCodeMotion();
58 isEnableOptConstantFolding_ = runtimeOptions.IsEnableOptConstantFolding();
59 isEnableCollectLiteralInfo_ = false;
60 isEnableLexenvSpecialization_ = runtimeOptions.IsEnableLexenvSpecialization();
61 isEnableNativeInline_ = runtimeOptions.IsEnableNativeInline();
62 isEnableLoweringBuiltin_ = runtimeOptions.IsEnableLoweringBuiltin();
63 isEnableOptBranchProfiling_ = runtimeOptions.IsEnableBranchProfiling();
64 }
65
HandleTargetCompilerMode(CompilationOptions & cOptions)66 bool AotCompilerPreprocessor::HandleTargetCompilerMode(CompilationOptions &cOptions)
67 {
68 if (runtimeOptions_.IsTargetCompilerMode()) {
69 if (!OhosPkgArgs::ParseArgs(*this, cOptions)) {
70 LOG_COMPILER(ERROR) << GetHelper();
71 LOG_COMPILER(ERROR) << "Parse pkg info failed, exit.";
72 return false;
73 }
74 const auto& mainPkgArgs = GetMainPkgArgs();
75 if (!mainPkgArgs) {
76 LOG_COMPILER(ERROR) << "No main pkg args found, exit";
77 return false;
78 }
79 if (!OhosPgoProcessor::MergeAndRemoveRuntimeAp(cOptions, mainPkgArgs)) {
80 LOG_COMPILER(ERROR) << "Fusion runtime ap failed, exit";
81 return false;
82 }
83 HandleTargetModeInfo(cOptions);
84 }
85 return true;
86 }
87
HandleTargetModeInfo(CompilationOptions & cOptions)88 void AotCompilerPreprocessor::HandleTargetModeInfo(CompilationOptions &cOptions)
89 {
90 JSRuntimeOptions &vmOpt = vm_->GetJSOptions();
91 ASSERT(vmOpt.IsTargetCompilerMode());
92 // target need fast compiler mode
93 vmOpt.SetFastAOTCompileMode(true);
94 vmOpt.SetOptLevel(DEFAULT_OPT_LEVEL);
95 cOptions.optLevel_ = DEFAULT_OPT_LEVEL;
96 }
97
HandlePandaFileNames(const int argc,const char ** argv)98 bool AotCompilerPreprocessor::HandlePandaFileNames(const int argc, const char **argv)
99 {
100 if (runtimeOptions_.GetCompilerPkgJsonInfo().empty() || pkgsArgs_.empty()) {
101 // if no pkgArgs, last param must be abc file
102 std::string files = argv[argc - 1];
103 if (!base::StringHelper::EndsWith(files, ".abc")) {
104 LOG_COMPILER(ERROR) << "The last argument must be abc file" << std::endl;
105 LOG_COMPILER(ERROR) << GetHelper();
106 return false;
107 }
108 std::string delimiter = GetFileDelimiter();
109 pandaFileNames_ = base::StringHelper::SplitString(files, delimiter);
110 }
111 return true;
112 }
113
AOTInitialize()114 void AotCompilerPreprocessor::AOTInitialize()
115 {
116 BytecodeStubCSigns::Initialize();
117 CommonStubCSigns::Initialize();
118 RuntimeStubCSigns::Initialize();
119 vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->Initialize();
120 }
121
SetShouldCollectLiteralInfo(CompilationOptions & cOptions,const CompilerLog * log)122 void AotCompilerPreprocessor::SetShouldCollectLiteralInfo(CompilationOptions &cOptions, const CompilerLog *log)
123 {
124 TSManager *tsManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager();
125 cOptions.isEnableCollectLiteralInfo_ = cOptions.isEnableTypeInfer_ &&
126 (profilerDecoder_.IsLoaded() || tsManager->AssertTypes() || log->OutputType());
127 }
128
GenerateAbcFileInfos()129 bool AotCompilerPreprocessor::GenerateAbcFileInfos()
130 {
131 size_t size = pandaFileNames_.size();
132 uint32_t checksum = 0;
133 for (size_t i = 0; i < size; ++i) {
134 const auto &fileName = pandaFileNames_.at(i);
135 auto extendedFilePath = panda::os::file::File::GetExtendedFilePath(fileName);
136 std::shared_ptr<JSPandaFile> jsPandaFile = CreateAndVerifyJSPandaFile(extendedFilePath);
137 AbcFileInfo fileInfo(extendedFilePath, jsPandaFile);
138 if (jsPandaFile == nullptr) {
139 LOG_COMPILER(ERROR) << "Cannot execute panda file '" << extendedFilePath << "'";
140 continue;
141 }
142 checksum = jsPandaFile->GetChecksum();
143 ResolveModule(jsPandaFile.get(), extendedFilePath);
144 fileInfos_.emplace_back(fileInfo);
145 }
146
147 return PGOProfilerManager::MergeApFiles(checksum, profilerDecoder_);
148 }
149
CreateAndVerifyJSPandaFile(const std::string & fileName)150 std::shared_ptr<JSPandaFile> AotCompilerPreprocessor::CreateAndVerifyJSPandaFile(const std::string &fileName)
151 {
152 JSPandaFileManager *jsPandaFileManager = JSPandaFileManager::GetInstance();
153 std::shared_ptr<JSPandaFile> jsPandaFile = nullptr;
154 if (runtimeOptions_.IsTargetCompilerMode()) {
155 auto pkgArgsIter = pkgsArgs_.find(fileName);
156 if (pkgArgsIter == pkgsArgs_.end()) {
157 LOG_COMPILER(ERROR) << "Can not find file in ohos pkgs args. file name: " << fileName;
158 return nullptr;
159 }
160 if (!(pkgArgsIter->second->GetJSPandaFile(runtimeOptions_, jsPandaFile))) {
161 return nullptr;
162 }
163 } else {
164 jsPandaFile = jsPandaFileManager->OpenJSPandaFile(fileName.c_str());
165 }
166 if (jsPandaFile == nullptr) {
167 LOG_ECMA(ERROR) << "open file " << fileName << " error";
168 return nullptr;
169 }
170
171 if (!jsPandaFile->IsNewVersion()) {
172 LOG_COMPILER(ERROR) << "AOT only support panda file with new ISA, while the '" <<
173 fileName << "' file is the old version";
174 return nullptr;
175 }
176
177 jsPandaFileManager->AddJSPandaFileVm(vm_, jsPandaFile);
178 return jsPandaFile;
179 }
180
ResolveModule(const JSPandaFile * jsPandaFile,const std::string & fileName)181 void AotCompilerPreprocessor::ResolveModule(const JSPandaFile *jsPandaFile, const std::string &fileName)
182 {
183 const auto &recordInfo = jsPandaFile->GetJSRecordInfo();
184 JSThread *thread = vm_->GetJSThread();
185 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
186 [[maybe_unused]] EcmaHandleScope scope(thread);
187 for (auto info: recordInfo) {
188 if (jsPandaFile->IsModule(info.second)) {
189 auto recordName = info.first;
190 JSHandle<JSTaggedValue> moduleRecord = moduleManager->HostResolveImportedModuleWithMerge(fileName.c_str(),
191 recordName);
192 RETURN_IF_ABRUPT_COMPLETION(thread);
193 SourceTextModule::Instantiate(thread, moduleRecord);
194 }
195 }
196 }
197
GenerateGlobalTypes(const CompilationOptions & cOptions)198 void AotCompilerPreprocessor::GenerateGlobalTypes(const CompilationOptions &cOptions)
199 {
200 for (const AbcFileInfo &fileInfo : fileInfos_) {
201 JSPandaFile *jsPandaFile = fileInfo.jsPandaFile_.get();
202 TSManager *tsManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager();
203 PGOTypeManager *ptManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetPTManager();
204 BytecodeInfoCollector collector(vm_, jsPandaFile, profilerDecoder_, cOptions.maxAotMethodSize_,
205 cOptions.isEnableCollectLiteralInfo_);
206 BCInfo &bytecodeInfo = collector.GetBytecodeInfo();
207 const PGOBCInfo *bcInfo = collector.GetPGOBCInfo();
208 const auto &methodPcInfos = bytecodeInfo.GetMethodPcInfos();
209 auto &methodList = bytecodeInfo.GetMethodList();
210 for (const auto &method : methodList) {
211 uint32_t methodOffset = method.first;
212 tsManager->SetCurConstantPool(jsPandaFile, methodOffset);
213 CString recordName = MethodLiteral::GetRecordName(jsPandaFile, EntityId(methodOffset));
214 auto methodLiteral = jsPandaFile->FindMethodLiteral(methodOffset);
215 auto &methodInfo = methodList.at(methodOffset);
216 auto &methodPcInfo = methodPcInfos[methodInfo.GetMethodPcInfoIndex()];
217 TypeRecorder typeRecorder(jsPandaFile, methodLiteral, tsManager, recordName, &profilerDecoder_,
218 methodPcInfo, collector.GetByteCodes(), cOptions.isEnableOptTrackField_);
219 typeRecorder.BindPgoTypeToGateType(jsPandaFile, tsManager, methodLiteral);
220
221 bcInfo->IterateInfoByType(methodOffset, PGOBCInfo::Type::ARRAY_LITERAL,
222 [this, tsManager, ptManager,
223 &recordName]([[maybe_unused]] const uint32_t bcIdx,
224 [[maybe_unused]] const uint32_t bcOffset, const uint32_t cpIdx) {
225 JSHandle<ConstantPool> constpoolHandle(tsManager->GetConstantPool());
226 JSThread *thread = vm_->GetJSThread();
227 JSTaggedValue arr =
228 ConstantPool::GetLiteralFromCache<ConstPoolType::ARRAY_LITERAL>(
229 thread, constpoolHandle.GetTaggedValue(), cpIdx, recordName);
230 JSHandle<JSArray> arrayHandle(thread, arr);
231 panda_file::File::EntityId id =
232 ConstantPool::GetIdFromCache(constpoolHandle.GetTaggedValue(), cpIdx);
233 ptManager->RecordElements(id, arrayHandle->GetElements());
234 });
235 }
236 }
237 }
238
GeneratePGOTypes(const CompilationOptions & cOptions)239 void AotCompilerPreprocessor::GeneratePGOTypes(const CompilationOptions &cOptions)
240 {
241 PGOTypeManager *ptManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetPTManager();
242 for (const AbcFileInfo &fileInfo : fileInfos_) {
243 JSPandaFile *jsPandaFile = fileInfo.jsPandaFile_.get();
244 BytecodeInfoCollector collector(vm_, jsPandaFile, profilerDecoder_, cOptions.maxAotMethodSize_,
245 cOptions.isEnableCollectLiteralInfo_);
246 PGOTypeParser parser(profilerDecoder_, ptManager);
247 parser.CreatePGOType(collector);
248 }
249 }
250
SnapshotInitialize()251 void AotCompilerPreprocessor::SnapshotInitialize()
252 {
253 PGOTypeManager *ptManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetPTManager();
254 ptManager->InitAOTSnapshot(fileInfos_.size());
255 }
256
GetMainPkgArgsAppSignature() const257 std::string AotCompilerPreprocessor::GetMainPkgArgsAppSignature() const
258 {
259 return GetMainPkgArgs() == nullptr ? "" : GetMainPkgArgs()->GetAppSignature();
260 }
261 } // namespace panda::ecmascript::kungfu