/* * Copyright (c) 2023-2024 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/mem/machine_code.h" #include "ecmascript/compiler/aot_file/func_entry_des.h" #include "ecmascript/jit/jit.h" #if ECMASCRIPT_ENABLE_CAST_CHECK #include "ecmascript/js_tagged_value-inl.h" #endif namespace panda::ecmascript { static bool SetPageProtect(uint8_t *textStart, size_t dataSize) { if (!Jit::GetInstance()->IsEnableJitFort()) { constexpr size_t pageSize = 4096; uintptr_t startPage = AlignDown(reinterpret_cast(textStart), pageSize); uintptr_t endPage = AlignUp(reinterpret_cast(textStart) + dataSize, pageSize); size_t protSize = endPage - startPage; return PageProtect(reinterpret_cast(startPage), protSize, PAGE_PROT_EXEC_READWRITE); } return true; } static int MachineCodeCopyToCache([[maybe_unused]] const MachineCodeDesc &desc, [[maybe_unused]] uint8_t *pText) { #ifndef JIT_ENABLE_CODE_SIGN if (memcpy_s(pText, desc.codeSizeAlign, reinterpret_cast(desc.codeAddr), desc.codeSize) != EOK) { LOG_JIT(ERROR) << "memcpy failed in CopyToCache"; return false; } #endif return true; } bool MachineCode::SetText(const MachineCodeDesc &desc) { uint8_t *textStart = reinterpret_cast(GetText()); uint8_t *pText = textStart; if (desc.rodataSizeBeforeTextAlign != 0) { if (memcpy_s(pText, desc.rodataSizeBeforeTextAlign, reinterpret_cast(desc.rodataAddrBeforeText), desc.rodataSizeBeforeText) != EOK) { // LCOV_EXCL_BR_LINE LOG_JIT(ERROR) << "memcpy fail in copy fast jit code"; return false; } pText += desc.rodataSizeBeforeTextAlign; } if (!Jit::GetInstance()->IsEnableJitFort() || !Jit::GetInstance()->IsEnableAsyncCopyToFort() || !desc.isAsyncCompileMode) { if (MachineCodeCopyToCache(desc, pText) == false) { return false; } } pText += desc.codeSizeAlign; if (desc.rodataSizeAfterTextAlign != 0) { if (memcpy_s(pText, desc.rodataSizeAfterTextAlign, reinterpret_cast(desc.rodataAddrAfterText), desc.rodataSizeAfterText) != EOK) { // LCOV_EXCL_BR_LINE LOG_JIT(ERROR) << "memcpy fail in copy fast jit code"; return false; } } return true; } bool MachineCode::SetNonText(const MachineCodeDesc &desc, EntityId methodId) { uint8_t *textStart = reinterpret_cast(GetText()); uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress(); if (memcpy_s(stackmapAddr, desc.stackMapOrOffsetTableSize, reinterpret_cast(desc.stackMapOrOffsetTableAddr), desc.stackMapOrOffsetTableSize) != EOK) { // LCOV_EXCL_BR_LINE LOG_JIT(ERROR) << "memcpy fail in copy fast jit stackmap"; return false; } FuncEntryDes *funcEntry = reinterpret_cast(desc.funcEntryDesAddr); if (!funcEntry) { LOG_JIT(ERROR) << "funcEntry is null."; return false; } uint32_t cnt = desc.funcEntryDesSize / sizeof(FuncEntryDes); ASSERT(cnt <= 2); // 2: jsfunction + deoptimize stub for (uint32_t i = 0; i < cnt; i++) { if (methodId == EntityId(funcEntry->indexInKindOrMethodId_)) { uint64_t codeAddr = funcEntry->codeAddr_ + static_cast(reinterpret_cast(textStart)); SetFuncAddr(codeAddr); break; } funcEntry++; } SetIsFastCall(funcEntry->isFastCall_); SetFpDeltaPrevFrameSp(funcEntry->fpDeltaPrevFrameSp_); SetFuncSize(funcEntry->funcSize_); SetCalleeRegisterNum(funcEntry->calleeRegisterNum_); SetCalleeReg2OffsetArray(funcEntry->CalleeReg2Offset_); return true; } bool MachineCode::SetData(const MachineCodeDesc &desc, JSHandle &method, size_t dataSize) { DISALLOW_GARBAGE_COLLECTION; if (desc.codeType == MachineCodeType::BASELINE_CODE) { return SetBaselineCodeData(desc, method, dataSize); } SetOSROffset(MachineCode::INVALID_OSR_OFFSET); SetOsrDeoptFlag(false); SetOsrExecuteCnt(0); size_t instrSize = desc.rodataSizeBeforeTextAlign + desc.codeSizeAlign + desc.rodataSizeAfterTextAlign; SetInstructionsSize(instrSize); SetStackMapOrOffsetTableSize(desc.stackMapSizeAlign); SetPayLoadSizeInBytes(dataSize); if (Jit::GetInstance()->IsEnableJitFort()) { SetInstructionsAddr(desc.instructionsAddr); ASSERT(desc.instructionsAddr != 0); ASSERT(dataSize == (desc.funcEntryDesSizeAlign + desc.stackMapSizeAlign) || dataSize == (desc.funcEntryDesSizeAlign + instrSize + desc.stackMapSizeAlign)); } else { ASSERT(dataSize == (desc.funcEntryDesSizeAlign + instrSize + desc.stackMapSizeAlign)); } if (!SetText(desc)) { return false; } if (!SetNonText(desc, method->GetMethodId())) { return false; } uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress(); uint8_t *textStart = reinterpret_cast(GetText()); CString methodName = method->GetRecordNameStr() + "." + CString(method->GetMethodName()); LOG_JIT(DEBUG) << "Fast JIT MachineCode :" << methodName << ", " << " text addr:" << reinterpret_cast(GetText()) << ", size:" << instrSize << ", stackMap addr:" << reinterpret_cast(stackmapAddr) << ", size:" << desc.stackMapSizeAlign; if (!SetPageProtect(textStart, dataSize)) { LOG_JIT(ERROR) << "MachineCode::SetData SetPageProtect failed"; return false; } return true; } bool MachineCode::SetBaselineCodeData(const MachineCodeDesc &desc, JSHandle &method, size_t dataSize) { DISALLOW_GARBAGE_COLLECTION; size_t instrSizeAlign = desc.codeSizeAlign; SetInstructionsSize(instrSizeAlign); SetStackMapOrOffsetTableSize(desc.stackMapSizeAlign); if (Jit::GetInstance()->IsEnableJitFort()) { ASSERT(desc.instructionsAddr != 0); ASSERT(dataSize == (desc.stackMapSizeAlign) || // reg. obj dataSize == (instrSizeAlign + desc.stackMapSizeAlign)); // huge obj SetInstructionsAddr(desc.instructionsAddr); } else { ASSERT(dataSize == (instrSizeAlign + desc.stackMapSizeAlign)); } SetPayLoadSizeInBytes(dataSize); uint8_t *textStart = reinterpret_cast(GetText()); if (Jit::GetInstance()->IsEnableJitFort()) { // relax assert for now until machine code padding for JitFort resolved ASSERT(IsAligned(reinterpret_cast(textStart), TEXT_ALIGN) || IsAligned(reinterpret_cast(textStart), DATA_ALIGN)); } else { ASSERT(IsAligned(reinterpret_cast(textStart), TEXT_ALIGN)); } uint8_t *pText = textStart; if (!Jit::GetInstance()->IsEnableJitFort() || !Jit::GetInstance()->IsEnableAsyncCopyToFort() || !desc.isAsyncCompileMode) { if (MachineCodeCopyToCache(desc, pText) == false) { return false; } } pText += instrSizeAlign; uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress(); if (memcpy_s(stackmapAddr, desc.stackMapOrOffsetTableSize, reinterpret_cast(desc.stackMapOrOffsetTableAddr), desc.stackMapOrOffsetTableSize) != EOK) { // LCOV_EXCL_BR_LINE LOG_BASELINEJIT(ERROR) << "memcpy fail in copy fast baselineJIT offsetTable"; return false; } SetFuncAddr(reinterpret_cast(textStart)); CString methodName = method->GetRecordNameStr() + "." + CString(method->GetMethodName()); LOG_BASELINEJIT(DEBUG) << "BaselineCode :" << methodName << ", " << " text addr:" << reinterpret_cast(GetText()) << ", size:" << instrSizeAlign << ", stackMap addr: 0, size: 0"; if (!SetPageProtect(textStart, dataSize)) { LOG_BASELINEJIT(ERROR) << "MachineCode::SetBaseLineCodeData SetPageProtect failed"; return false; } return true; } bool MachineCode::IsInText(const uintptr_t pc) const { uintptr_t textStart = GetText(); uintptr_t textEnd = textStart + GetTextSize(); return textStart <= pc && pc < textEnd; } std::tuple MachineCode::CalCallSiteInfo() const { uintptr_t textStart = GetText(); uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress(); ASSERT(stackmapAddr != nullptr); int delta = GetFpDeltaPrevFrameSp(); kungfu::CalleeRegAndOffsetVec calleeRegInfo; for (uint32_t j = 0; j < GetCalleeRegisterNum(); j++) { kungfu::LLVMStackMapType::DwarfRegType reg = static_cast(GetCalleeReg2OffsetArray(2 * j)); kungfu::LLVMStackMapType::OffsetType offset = static_cast(GetCalleeReg2OffsetArray(2 * j + 1)); kungfu::LLVMStackMapType::DwarfRegAndOffsetType regAndOffset = std::make_pair(reg, offset); calleeRegInfo.emplace_back(regAndOffset); } auto ret = std::make_tuple(textStart, stackmapAddr, delta, calleeRegInfo); return ret; } uintptr_t MachineCode::GetText() const { if (Jit::GetInstance()->IsEnableJitFort()) { return GetInstructionsAddr(); } else { return GetNonTextAddress(); } } uint8_t *MachineCode::GetStackMapOrOffsetTableAddress() const { if (Jit::GetInstance()->IsEnableJitFort()) { // stackmap immediately follows MachineCode NonText area return reinterpret_cast(GetNonTextAddress()); } else { return reinterpret_cast(GetText() + GetInstructionsSize()); } } void MachineCode::ProcessMarkObject() { Region *region = Region::ObjectAddressToRange(this); Heap *localHeap = reinterpret_cast(region->GetLocalHeap()); if (!localHeap) { // it is a huge machine code object. skip return; } ASSERT(localHeap->GetMachineCodeSpace()); if (localHeap->GetMachineCodeSpace()) { localHeap->GetMachineCodeSpace()->MarkJitFortMemAlive(this); } } } // namespace panda::ecmascript