/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/file_loader.h" #include "ecmascript/base/config.h" #include "ecmascript/compiler/bc_call_signature.h" #include "ecmascript/compiler/common_stubs.h" #include "ecmascript/compiler/llvm/llvm_stackmap_parser.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/jspandafile/constpool_value.h" #include "ecmascript/jspandafile/js_pandafile.h" #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/js_runtime_options.h" #include "ecmascript/js_thread.h" #include "ecmascript/snapshot/mem/snapshot.h" namespace panda::ecmascript { void StubModulePackInfo::Save(const std::string &filename) { if (!VerifyFilePath(filename, true)) { return; } std::ofstream moduleFile(filename.c_str(), std::ofstream::binary); SetStubNum(entries_.size()); /* write stub entries offset */ moduleFile.write(reinterpret_cast(&entryNum_), sizeof(entryNum_)); moduleFile.write(reinterpret_cast(entries_.data()), sizeof(FuncEntryDes) * entryNum_); uint32_t moduleNum = GetCodeUnitsNum(); moduleFile.write(reinterpret_cast(&moduleNum), sizeof(moduleNum_)); moduleFile.write(reinterpret_cast(&totalCodeSize_), sizeof(totalCodeSize_)); for (size_t i = 0; i < moduleNum; i++) { moduleFile.write(reinterpret_cast(&(des_[i].hostCodeSectionAddr_)), sizeof(des_[i].hostCodeSectionAddr_)); moduleFile.write(reinterpret_cast(&(des_[i].codeSize_)), sizeof(des_[i].codeSize_)); moduleFile.write(reinterpret_cast(des_[i].GetHostCodeSecAddr()), des_[i].GetCodeSize()); moduleFile.write(reinterpret_cast(&(des_[i].stackMapSize_)), sizeof(des_[i].stackMapSize_)); moduleFile.write(reinterpret_cast(des_[i].GetStackMapSecAddr()), des_[i].GetStackMapSize()); } moduleFile.close(); } bool StubModulePackInfo::Load(EcmaVM *vm, const std::string &filename) { // now MachineCode is non movable, code and stackmap sperately is saved to MachineCode // by calling NewMachineCodeObject. // then MachineCode will support movable, code is saved to MachineCode and stackmap is saved // to different heap which will be freed when stackmap is parsed by EcmaVM is started. if (!VerifyFilePath(filename)) { COMPILER_LOG(ERROR) << "Can not load stub file from path [ " << filename << " ], " << "please execute ark_stub_compiler with options --stub-file."; return false; } std::ifstream moduleFile(filename.c_str(), std::ofstream::binary); if (!moduleFile.good()) { moduleFile.close(); return false; } moduleFile.read(reinterpret_cast(&entryNum_), sizeof(entryNum_)); entries_.resize(entryNum_); moduleFile.read(reinterpret_cast(entries_.data()), sizeof(FuncEntryDes) * entryNum_); moduleFile.read(reinterpret_cast(&moduleNum_), sizeof(moduleNum_)); des_.resize(moduleNum_); uint32_t totalCodeSize = 0; moduleFile.read(reinterpret_cast(&totalCodeSize), sizeof(totalCodeSize_)); auto factory = vm->GetFactory(); auto codeHandle = factory->NewMachineCodeObject(totalCodeSize, nullptr); SetCode(codeHandle); uint32_t curUnitOffset = 0; for (size_t i = 0; i < moduleNum_; i++) { moduleFile.read(reinterpret_cast(&(des_[i].hostCodeSectionAddr_)), sizeof(des_[i].hostCodeSectionAddr_)); moduleFile.read(reinterpret_cast(&(des_[i].codeSize_)), sizeof(des_[i].codeSize_)); uint32_t codeSize = des_[i].GetCodeSize(); // startAddr of current code unit on device side uintptr_t startAddr = codeHandle->GetDataOffsetAddress() + static_cast(curUnitOffset); moduleFile.read(reinterpret_cast(startAddr), codeSize); curUnitOffset += codeSize; des_[i].SetDeviceCodeSecAddr(startAddr); moduleFile.read(reinterpret_cast(&(des_[i].stackMapSize_)), sizeof(des_[i].stackMapSize_)); uint32_t stackmapSize = des_[i].GetStackMapSize(); std::unique_ptr stackmapPtr(std::make_unique(stackmapSize)); moduleFile.read(reinterpret_cast(stackmapPtr.get()), stackmapSize); if (stackmapSize != 0) { bool enableLog = vm->GetJSOptions().WasSetlogCompiledMethods(); kungfu::LLVMStackMapParser::GetInstance(enableLog).CalculateStackMap(std::move(stackmapPtr), des_[i].GetHostCodeSecAddr(), startAddr); } } for (auto &funcEntryDes : GetStubs()) { auto codeAddr = funcEntryDes.codeAddr_; // offset auto moduleIndex = funcEntryDes.moduleIndex_; auto startAddr = des_[moduleIndex].GetDeviceCodeSecAddr(); auto delta = funcEntryDes.fpDeltaPrevFramSp_; uintptr_t funAddr = startAddr + codeAddr; kungfu::Func2FpDelta fun2fpDelta; auto funSize = funcEntryDes.funcSize_; fun2fpDelta[funAddr] = std::make_pair(delta, funSize); kungfu::LLVMStackMapParser::GetInstance().CalculateFuncFpDelta(fun2fpDelta); } for (size_t i = 0; i < entries_.size(); i++) { auto des = des_[entries_[i].moduleIndex_]; entries_[i].codeAddr_ += des.GetDeviceCodeSecAddr(); } moduleFile.close(); COMPILER_LOG(INFO) << "Load stub file success"; return true; } void AOTModulePackInfo::Save(const std::string &filename) { if (!VerifyFilePath(filename, true)) { return; } std::ofstream moduleFile(filename.c_str(), std::ofstream::binary); SetStubNum(entries_.size()); moduleFile.write(reinterpret_cast(&entryNum_), sizeof(entryNum_)); moduleFile.write(reinterpret_cast(entries_.data()), sizeof(FuncEntryDes) * entryNum_); uint32_t moduleNum = GetCodeUnitsNum(); moduleFile.write(reinterpret_cast(&moduleNum), sizeof(moduleNum_)); moduleFile.write(reinterpret_cast(&totalCodeSize_), sizeof(totalCodeSize_)); moduleFile.write(reinterpret_cast(aotFileHashs_.data()), sizeof(uint32_t) * moduleNum); for (size_t i = 0; i < moduleNum; i++) { moduleFile.write(reinterpret_cast(&(des_[i].hostCodeSectionAddr_)), sizeof(des_[i].hostCodeSectionAddr_)); moduleFile.write(reinterpret_cast(&(des_[i].codeSize_)), sizeof(des_[i].codeSize_)); moduleFile.write(reinterpret_cast(des_[i].GetHostCodeSecAddr()), des_[i].GetCodeSize()); moduleFile.write(reinterpret_cast(&(des_[i].stackMapSize_)), sizeof(des_[i].stackMapSize_)); moduleFile.write(reinterpret_cast(des_[i].GetStackMapSecAddr()), des_[i].GetStackMapSize()); } moduleFile.close(); } bool AOTModulePackInfo::Load(EcmaVM *vm, const std::string &filename) { if (!VerifyFilePath(filename)) { COMPILER_LOG(ERROR) << "Can not load aot file from path [ " << filename << " ], " << "please execute ark_aot_compiler with options --aot-file."; return false; } std::ifstream moduleFile(filename.c_str(), std::ofstream::binary); if (!moduleFile.good()) { moduleFile.close(); return false; } moduleFile.read(reinterpret_cast(&entryNum_), sizeof(entryNum_)); entries_.resize(entryNum_); moduleFile.read(reinterpret_cast(entries_.data()), sizeof(FuncEntryDes) * entryNum_); moduleFile.read(reinterpret_cast(&moduleNum_), sizeof(moduleNum_)); des_.resize(moduleNum_); aotFileHashs_.resize(moduleNum_); uint32_t totalCodeSize = 0; moduleFile.read(reinterpret_cast(&totalCodeSize), sizeof(totalCodeSize_)); [[maybe_unused]] EcmaHandleScope handleScope(vm->GetAssociatedJSThread()); auto factory = vm->GetFactory(); auto codeHandle = factory->NewMachineCodeObject(totalCodeSize, nullptr); SetCode(codeHandle); moduleFile.read(reinterpret_cast(aotFileHashs_.data()), sizeof(uint32_t) * moduleNum_); uint32_t curUnitOffset = 0; for (size_t i = 0; i < moduleNum_; i++) { moduleFile.read(reinterpret_cast(&(des_[i].hostCodeSectionAddr_)), sizeof(des_[i].hostCodeSectionAddr_)); moduleFile.read(reinterpret_cast(&(des_[i].codeSize_)), sizeof(des_[i].codeSize_)); uint32_t codeSize = des_[i].GetCodeSize(); uintptr_t startAddr = codeHandle->GetDataOffsetAddress() + static_cast(curUnitOffset); moduleFile.read(reinterpret_cast(startAddr), codeSize); curUnitOffset += codeSize; des_[i].SetDeviceCodeSecAddr(startAddr); moduleFile.read(reinterpret_cast(&(des_[i].stackMapSize_)), sizeof(des_[i].stackMapSize_)); uint32_t stackmapSize = des_[i].GetStackMapSize(); std::unique_ptr stackmapPtr(std::make_unique(stackmapSize)); moduleFile.read(reinterpret_cast(stackmapPtr.get()), stackmapSize); if (stackmapSize != 0) { bool enableLog = vm->GetJSOptions().WasSetlogCompiledMethods(); kungfu::LLVMStackMapParser::GetInstance(enableLog).CalculateStackMap(std::move(stackmapPtr), des_[i].GetHostCodeSecAddr(), startAddr); } } for (auto &funcEntryDes : GetStubs()) { auto codeAddr = funcEntryDes.codeAddr_; // offset auto moduleIndex = funcEntryDes.moduleIndex_; auto delta = funcEntryDes.fpDeltaPrevFramSp_; auto funSize = funcEntryDes.funcSize_; auto startAddr = des_[moduleIndex].GetDeviceCodeSecAddr(); uintptr_t funAddr = startAddr + codeAddr; kungfu::Func2FpDelta fun2fpDelta; fun2fpDelta[funAddr] = std::make_pair(delta, funSize); kungfu::LLVMStackMapParser::GetInstance().CalculateFuncFpDelta(fun2fpDelta); } for (size_t i = 0; i < entries_.size(); i++) { auto des = des_[entries_[i].moduleIndex_]; entries_[i].codeAddr_ += des.GetDeviceCodeSecAddr(); auto curFileHash = aotFileHashs_[entries_[i].moduleIndex_]; auto curMethodId = entries_[i].indexInKind_; vm->SetAOTFuncEntry(curFileHash, curMethodId, entries_[i].codeAddr_); } moduleFile.close(); COMPILER_LOG(INFO) << "Load aot file success"; return true; } void ModulePackInfo::Iterate(const RootVisitor &v) { v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&machineCodeObj_))); } bool ModulePackInfo::VerifyFilePath([[maybe_unused]] const std::string &filePath, [[maybe_unused]] bool toGenerate) const { #ifndef PANDA_TARGET_WINDOWS if (filePath.size() > PATH_MAX) { return false; } std::vector resolvedPath(PATH_MAX); auto result = realpath(filePath.c_str(), resolvedPath.data()); if (toGenerate && errno == ENOENT) { return true; } if (result == nullptr) { return false; } return true; #else return false; #endif } void FileLoader::LoadStubFile(const std::string &fileName) { if (!stubPackInfo_.Load(vm_, fileName)) { return; } auto stubs = stubPackInfo_.GetStubs(); InitializeStubEntries(stubs); } void FileLoader::LoadAOTFile(const std::string &fileName) { AOTModulePackInfo aotPackInfo_; if (!aotPackInfo_.Load(vm_, fileName)) { return; } AddAOTPackInfo(aotPackInfo_); } void FileLoader::TryLoadSnapshotFile() { const CString snapshotPath(vm_->GetJSOptions().GetSnapshotOutputFile().c_str()); Snapshot snapshot(vm_); #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) snapshot.Deserialize(SnapshotType::TS_LOADER, snapshotPath); #endif } bool FileLoader::hasLoaded(const JSPandaFile *jsPandaFile) { auto fileHash = jsPandaFile->GetPandaFile()->GetFilenameHash(); return !(hashToEntryMap_.find(fileHash) == hashToEntryMap_.end()); } void FileLoader::UpdateJSMethods(JSHandle mainFunc, const JSPandaFile *jsPandaFile) { // get generated const pool in execution phase auto thread = vm_->GetAssociatedJSThread(); JSHandle constPool(thread, mainFunc->GetConstantPool()); ConstantPool *curPool = ConstantPool::Cast(constPool->GetTaggedObject()); // get main func method auto mainFuncMethodId = jsPandaFile->GetMainMethodIndex(); auto fileHash = jsPandaFile->GetPandaFile()->GetFilenameHash(); auto mainEntry = GetAOTFuncEntry(fileHash, mainFuncMethodId); // 1 : default para number JSMethod *mainMethod = jsPandaFile->FindMethods(mainFuncMethodId); mainMethod->SetAotCodeBit(true); mainMethod->SetNativeBit(false); mainFunc->SetCodeEntry(reinterpret_cast(mainEntry)); const CUnorderedMap &constpoolMap = jsPandaFile->GetConstpoolMap(); for (const auto &it : constpoolMap) { ConstPoolValue value(it.second); if (value.GetConstpoolType() == ConstPoolType::BASE_FUNCTION || value.GetConstpoolType() == ConstPoolType::NC_FUNCTION || value.GetConstpoolType() == ConstPoolType::GENERATOR_FUNCTION || value.GetConstpoolType() == ConstPoolType::ASYNC_FUNCTION || value.GetConstpoolType() == ConstPoolType::METHOD) { auto id = value.GetConstpoolIndex(); auto codeEntry = GetAOTFuncEntry(fileHash, it.first); JSMethod *curMethod = jsPandaFile->FindMethods(it.first); curMethod->SetAotCodeBit(true); curMethod->SetNativeBit(false); auto curFunction = JSFunction::Cast(curPool->GetObjectFromCache(id).GetTaggedObject()); curFunction->SetCodeEntry(reinterpret_cast(codeEntry)); } } } void FileLoader::AdjustBCStubAndDebuggerStubEntries(JSThread *thread, const std::vector &stubs, const AsmInterParsedOption &asmInterOpt) { auto defaultBCStubDes = stubs[BytecodeStubCSigns::SingleStepDebugging]; auto defaultNonexistentBCStubDes = stubs[BytecodeStubCSigns::HandleOverflow]; auto defaultBCDebuggerStubDes = stubs[BytecodeStubCSigns::BCDebuggerEntry]; auto defaultBCDebuggerExceptionStubDes = stubs[BytecodeStubCSigns::BCDebuggerExceptionEntry]; ASSERT(defaultBCStubDes.kind_ == CallSignature::TargetKind::BYTECODE_HELPER_HANDLER); thread->SetUnrealizedBCStubEntry(defaultBCStubDes.codeAddr_); thread->SetNonExistedBCStubEntry(defaultNonexistentBCStubDes.codeAddr_); #define UNDEF_STUB(name) \ thread->SetBCStubEntry(BytecodeStubCSigns::ID_##name, defaultBCStubDes.codeAddr_); INTERPRETER_IGNORED_BC_STUB_LIST(UNDEF_STUB) #undef UNDEF_STUB for (int i = asmInterOpt.handleStart; i <= asmInterOpt.handleEnd && i >= 0; i++) { thread->SetBCStubEntry(static_cast(i), defaultBCStubDes.codeAddr_); } // bc debugger stub entries thread->SetNonExistedBCDebugStubEntry(defaultNonexistentBCStubDes.codeAddr_); for (size_t i = 0; i < BCStubEntries::EXISTING_BC_HANDLER_STUB_ENTRIES_COUNT; i++) { if (i == BytecodeStubCSigns::ID_ExceptionHandler) { thread->SetBCDebugStubEntry(i, defaultBCDebuggerExceptionStubDes.codeAddr_); continue; } thread->SetBCDebugStubEntry(i, defaultBCDebuggerStubDes.codeAddr_); } } void FileLoader::InitializeStubEntries(const std::vector& stubs) { auto thread = vm_->GetAssociatedJSThread(); for (size_t i = 0; i < stubs.size(); i++) { auto des = stubs[i]; if (des.IsCommonStub()) { thread->SetFastStubEntry(des.indexInKind_, des.codeAddr_); } else if (des.IsBCStub()) { thread->SetBCStubEntry(des.indexInKind_, des.codeAddr_); #if ECMASCRIPT_ENABLE_ASM_INTERPRETER_LOG std::cout << "bytecode: " << GetEcmaOpcodeStr(static_cast(des.indexInKind_)) << " addr:" << des.codeAddr_ << std::endl; #endif } else { thread->RegisterRTInterface(des.indexInKind_, des.codeAddr_); #if ECMASCRIPT_ENABLE_ASM_INTERPRETER_LOG std::cout << "runtime index: " << des.indexInKind_ << " addr:" << des.codeAddr_ << std::endl; #endif } } AsmInterParsedOption asmInterOpt = vm_->GetJSOptions().GetAsmInterParsedOption(); AdjustBCStubAndDebuggerStubEntries(thread, stubs, asmInterOpt); } }