* 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/jit/jit_task.h"
#include "ecmascript/base/config.h"
#include "common_components/heap/heap_manager.h"
#include "ecmascript/jspandafile/program_object.h"
#include "ecmascript/ohos/jit_tools.h"
#include "ecmascript/compiler/jit_compilation_env.h"
#include "ecmascript/platform/file.h"
namespace panda::ecmascript {
JitTaskpool *JitTaskpool::GetCurrentTaskpool()
{
static JitTaskpool *taskpool = new JitTaskpool();
return taskpool;
}
uint32_t JitTaskpool::TheMostSuitableThreadNum([[maybe_unused]]uint32_t threadNum) const
{
return 1;
}
JitTask::JitTask(JSThread *hostThread, JSThread *compilerThread, Jit *jit, JSHandle<JSFunction> &jsFunction,
CompilerTier tier, CString &methodName, int32_t offset, JitCompileMode mode)
: hostThread_(hostThread),
compilerThread_(compilerThread),
jit_(jit),
jsFunction_(jsFunction),
compilerTask_(nullptr),
state_(CompileState::SUCCESS),
compilerTier_(tier),
methodName_(methodName),
offset_(offset),
jitCompileMode_(mode),
runState_(RunState::INIT)
{
LOG_JIT(DEBUG) << "JitTask compilerThread addr: " << compilerThread_;
jit->IncJitTaskCnt(hostThread);
dependencies_ = new kungfu::LazyDeoptAllDependencies();
sustainingJSHandle_ = std::make_unique<SustainingJSHandle>(hostThread->GetEcmaVM());
}
void JitTask::PrepareCompile()
{
CloneProfileTypeInfo();
SustainingJSHandles();
compilerTask_ = jit_->CreateJitCompilerTask(this);
Method *method = Method::Cast(jsFunction_->GetMethod(hostThread_).GetTaggedObject());
JSTaggedValue constpool = method->GetConstantPool(hostThread_);
if (!ConstantPool::CheckUnsharedConstpool(constpool)) {
hostThread_->GetEcmaVM()->FindOrCreateUnsharedConstpool(constpool);
}
SetRunState(RunState::INIT);
}
void JitTask::Optimize()
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "JIT::Compiler frontend", "");
bool res = jit_->JitCompile(compilerTask_, this);
if (!res) {
SetCompileFailed();
}
}
void JitTask::Finalize()
{
if (!IsCompileSuccess()) {
return;
}
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "JIT::Compiler backend", "");
bool res = jit_->JitFinalize(compilerTask_, this);
if (!res) {
SetCompileFailed();
}
}
void JitTask::InstallOsrCode(JSHandle<MachineCode> &codeObj)
{
auto profile = jsFunction_->GetProfileTypeInfo(hostThread_);
if (profile.IsUndefined()) {
LOG_JIT(DEBUG) << "[OSR] Empty profile for installing code:" << GetMethodName();
return;
}
jsFunction_->SetIsCompiledFastCall(codeObj->GetIsFastCall());
JSHandle<ProfileTypeInfo> profileInfoHandle =
JSHandle<ProfileTypeInfo>::Cast(JSHandle<JSTaggedValue>(hostThread_, profile));
uint32_t slotId = profileInfoHandle->GetIcSlotToOsrLength() - 1;
auto profileData = profileInfoHandle->Get(hostThread_, slotId);
auto factory = hostThread_->GetEcmaVM()->GetFactory();
if (!profileData.IsTaggedArray()) {
const uint32_t initLen = 1;
JSHandle<TaggedArray> newArr = factory->NewTaggedArray(initLen);
newArr->Set(hostThread_, 0, codeObj.GetTaggedValue());
profileInfoHandle->Set(hostThread_, slotId, newArr.GetTaggedValue());
LOG_JIT(DEBUG) << "[OSR] Install machine code:" << GetMethodName()
<< ", code address: " << reinterpret_cast<void*>(codeObj->GetFuncAddr())
<< ", index: " << newArr->GetLength() - 1;
return;
}
JSHandle<TaggedArray> arr(hostThread_, profileData);
JSHandle<TaggedArray> newArr = factory->NewTaggedArray(arr->GetLength() + 1);
uint32_t i = 0;
for (; i < arr->GetLength(); i++) {
newArr->Set(hostThread_, i, arr->Get(hostThread_, i));
}
newArr->Set(hostThread_, i, codeObj.GetTaggedValue());
profileInfoHandle->Set(hostThread_, slotId, newArr.GetTaggedValue());
LOG_JIT(DEBUG) << "[OSR] Install machine code:" << GetMethodName()
<< ", code address: " << reinterpret_cast<void*>(codeObj->GetFuncAddr())
<< ", index: " << newArr->GetLength() - 1;
return;
}
static void ComputeAlignedSizes(MachineCodeDesc &desc)
{
desc.funcEntryDesSizeAlign = AlignUp(desc.funcEntryDesSize, MachineCode::TEXT_ALIGN);
desc.stackMapSizeAlign = AlignUp(desc.stackMapOrOffsetTableSize, MachineCode::DATA_ALIGN);
desc.heapConstantTableSizeAlign = AlignUp(desc.heapConstantTableSize, MachineCode::DATA_ALIGN);
desc.rodataSizeBeforeTextAlign = AlignUp(desc.rodataSizeBeforeText, MachineCode::TEXT_ALIGN);
if (desc.codeType == MachineCodeType::BASELINE_CODE ||
desc.codeType == MachineCodeType::ARKSTEED_CODE) {
desc.codeSizeAlign = Jit::GetInstance()->IsEnableJitFort() ?
AlignUp(desc.codeSize, MachineCode::TEXT_ALIGN) :
AlignUp(desc.codeSize, MachineCode::DATA_ALIGN);
return;
}
if (Jit::GetInstance()->IsEnableJitFort()) {
if (desc.rodataSizeAfterText) {
desc.codeSizeAlign = AlignUp(desc.codeSize, MachineCode::DATA_ALIGN);
desc.rodataSizeAfterTextAlign = AlignUp(desc.rodataSizeAfterText, MachineCode::TEXT_ALIGN);
} else {
desc.codeSizeAlign = AlignUp(desc.codeSize, MachineCode::TEXT_ALIGN);
}
} else {
desc.codeSizeAlign = AlignUp(desc.codeSize, MachineCode::DATA_ALIGN);
desc.rodataSizeAfterTextAlign = AlignUp(desc.rodataSizeAfterText, MachineCode::DATA_ALIGN);
}
}
size_t JitTask::ComputePayLoadSize(MachineCodeDesc &codeDesc)
{
ComputeAlignedSizes(codeDesc);
if (codeDesc.codeType == MachineCodeType::BASELINE_CODE) {
if (Jit::GetInstance()->IsEnableJitFort()) {
size_t payLoadSize = codeDesc.stackMapSizeAlign + codeDesc.codeSizeAlign;
size_t allocSize = AlignUp(payLoadSize + MachineCode::SIZE,
static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
codeDesc.instructionsSize = codeDesc.codeSizeAlign;
LOG_JIT(DEBUG) << "ComputePayLoadSize:: MachineCode Object size to allocate: " << allocSize
<< " (instruction size): " << codeDesc.codeSizeAlign;
if (allocSize > g_maxRegularHeapObjectSize) {
return payLoadSize;
} else {
return payLoadSize - codeDesc.codeSizeAlign;
}
} else {
return codeDesc.stackMapSizeAlign + codeDesc.codeSizeAlign;
}
}
if (codeDesc.codeType == MachineCodeType::ARKSTEED_CODE) {
if (Jit::GetInstance()->IsEnableJitFort()) {
size_t payLoadSize = codeDesc.codeSizeAlign + codeDesc.stackMapSizeAlign +
codeDesc.heapConstantTableSizeAlign;
size_t allocSize = AlignUp(payLoadSize + MachineCode::SIZE,
static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
codeDesc.instructionsSize = codeDesc.codeSizeAlign;
LOG_JIT(DEBUG) << "InstallCode:: ArkSteed MachineCode Object size to allocate: "
<< allocSize << " (instruction size): " << codeDesc.codeSizeAlign;
if (allocSize > g_maxRegularHeapObjectSize) {
codeDesc.isHugeObj = true;
return payLoadSize;
} else {
return payLoadSize - codeDesc.codeSizeAlign;
}
} else {
return codeDesc.codeSizeAlign + codeDesc.stackMapSizeAlign + codeDesc.heapConstantTableSizeAlign;
}
}
ASSERT(codeDesc.codeType == MachineCodeType::FAST_JIT_CODE);
if (Jit::GetInstance()->IsEnableJitFort()) {
size_t instructionsSize =
codeDesc.rodataSizeBeforeTextAlign + codeDesc.codeSizeAlign + codeDesc.rodataSizeAfterTextAlign;
size_t payLoadSize = codeDesc.funcEntryDesSizeAlign + instructionsSize +
codeDesc.stackMapSizeAlign + codeDesc.heapConstantTableSizeAlign;
size_t allocSize = AlignUp(payLoadSize + MachineCode::SIZE,
static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
LOG_JIT(DEBUG) << "ComputePayLoadSize:: MachineCode Object size to allocate: " << allocSize
<< " (instruction size): " << instructionsSize;
codeDesc.instructionsSize = instructionsSize;
if (allocSize > g_maxRegularHeapObjectSize) {
codeDesc.isHugeObj = true;
}
return payLoadSize - instructionsSize;
} else {
return codeDesc.funcEntryDesSizeAlign + codeDesc.rodataSizeBeforeTextAlign + codeDesc.codeSizeAlign +
codeDesc.rodataSizeAfterTextAlign + codeDesc.stackMapSizeAlign + codeDesc.heapConstantTableSizeAlign;
}
}
void DumpJitCode(const JSThread *thread, JSHandle<MachineCode> &machineCode, JSHandle<Method> &method)
{
#if !READ_BARRIER_INTRINSIC_DFX
if (!ohos::JitTools::GetJitDumpObjEanble()) {
return;
}
#endif
JsJitDumpElf jitDumpElf;
jitDumpElf.Init();
char *funcAddr = reinterpret_cast<char *>(machineCode->GetFuncAddr());
size_t len = machineCode->GetTextSize();
std::vector<uint8> vec(len);
if (memmove_s(vec.data(), len, funcAddr, len) != EOK) {
LOG_JIT(DEBUG) << "Fail to get machineCode on function addr: " << funcAddr;
}
jitDumpElf.AppendData(vec);
const char *filename = method->GetMethodName(thread);
std::string fileName = std::string(filename);
uintptr_t addr = machineCode->GetFuncAddr();
fileName = fileName + "_" + std::to_string(addr) + "+" + std::to_string(len);
jitDumpElf.AppendSymbolToSymTab(0, 0, len, std::string(filename));
#if READ_BARRIER_INTRINSIC_DFX
std::string outFile = "./" + std::string(fileName);
#else
std::string realOutPath;
std::string sanboxPath = panda::os::file::File::GetExtendedFilePath(AotCrashInfo::GetSandBoxPath());
if (!ecmascript::RealPath(sanboxPath, realOutPath, false)) {
return;
}
std::string outFile = realOutPath + "/" + std::string(fileName);
if (!ecmascript::FileExist(outFile.c_str())) {
return;
}
#endif
int fd = open(outFile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
FdsanExchangeOwnerTag(reinterpret_cast<fd_t>(fd));
jitDumpElf.WriteJitElfFile(fd);
Close(reinterpret_cast<fd_t>(fd));
}
static void FillHeapConstantTable(JSHandle<MachineCode> &machineCodeObj, const MachineCodeDesc &codeDesc)
{
if (codeDesc.heapConstantTableAddr == 0) {
return;
}
ASSERT(!g_isEnableCMCGC);
uint64_t *heapConstantTableAddr = reinterpret_cast<uint64_t*>(machineCodeObj->GetHeapConstantTableAddress());
JSHandle<JSTaggedValue> *heapConstantTableInCodeDesc =
reinterpret_cast<JSHandle<JSTaggedValue>*>(codeDesc.heapConstantTableAddr);
uint64_t constTableSlotNum = codeDesc.heapConstantTableSize / sizeof(uint64_t);
LOG_JIT(DEBUG) << "constant table size: " << constTableSlotNum << "\n";
for (uint64_t i = 0; i < constTableSlotNum; ++i) {
JSHandle<JSTaggedValue> heapObj = heapConstantTableInCodeDesc[i];
heapConstantTableAddr[i] = heapObj->GetRawData();
Region *heapObjRegion = Region::ObjectAddressToRange(heapObj->GetRawData());
Region *curMachineCodeObjRegion =
Region::ObjectAddressToRange(machineCodeObj.GetTaggedValue().GetRawHeapObject());
if (heapObjRegion->InYoungSpace()) {
curMachineCodeObjRegion->InsertOldToNewRSet(reinterpret_cast<uintptr_t>(&(heapConstantTableAddr[i])));
} else if (heapObjRegion->InSharedHeap()) {
curMachineCodeObjRegion->InsertLocalToShareRSet(reinterpret_cast<uintptr_t>(&(heapConstantTableAddr[i])));
}
}
}
void JitTask::InstallCode()
{
if (!IsCompileSuccess()) {
return;
}
[[maybe_unused]] EcmaHandleScope handleScope(hostThread_);
JSHandle<Method> methodHandle(hostThread_, Method::Cast(jsFunction_->GetMethod(hostThread_).GetTaggedObject()));
size_t size = ComputePayLoadSize(codeDesc_);
codeDesc_.isAsyncCompileMode = IsAsyncTask();
if (!kungfu::LazyDeoptAllDependencies::Commit(
GetDependencies(), hostThread_, jsFunction_.GetTaggedValue())) {
return;
}
JSHandle<MachineCode> machineCodeObj;
if (Jit::GetInstance()->IsEnableJitFort()) {
TaggedObject *machineCode = hostThread_->GetEcmaVM()->GetFactory()->NewMachineCodeObject(size, codeDesc_);
if (machineCode == nullptr) {
LOG_JIT(DEBUG) << "InstallCode skipped. NewMachineCode NULL for size " << size;
if (hostThread_->HasPendingException()) {
hostThread_->SetMachineCodeLowMemory(true);
hostThread_->ClearExceptionAndExtraErrorMessage();
}
return;
}
machineCodeObj = hostThread_->GetEcmaVM()->GetFactory()->SetMachineCodeObjectData(
machineCode, size, codeDesc_, methodHandle);
} else {
machineCodeObj = hostThread_->GetEcmaVM()->GetFactory()->NewMachineCodeObject(
size, codeDesc_, methodHandle);
}
if (machineCodeObj.GetAddress() == ToUintPtr(nullptr)) {
return;
}
#if READ_BARRIER_INTRINSIC_DFX
DumpJitCode(hostThread_, machineCodeObj, methodHandle);
#endif
machineCodeObj->SetOSROffset(offset_);
FillHeapConstantTable(machineCodeObj, codeDesc_);
if (hostThread_->HasPendingException()) {
hostThread_->SetMachineCodeLowMemory(true);
hostThread_->ClearExceptionAndExtraErrorMessage();
}
if (IsOsrTask()) {
InstallOsrCode(machineCodeObj);
} else {
InstallCodeByCompilerTier(machineCodeObj, methodHandle);
}
uintptr_t codeAddr = machineCodeObj->GetFuncAddr();
uintptr_t codeAddrEnd = codeAddr + machineCodeObj->GetInstructionsSize();
__builtin___clear_cache(reinterpret_cast<char *>(codeAddr), reinterpret_cast<char*>(codeAddrEnd));
if (Jit::GetInstance()->IsEnableJitFort()) {
if (g_isEnableCMCGC) {
common::BaseRuntime::GetInstance()->GetHeapManager().MarkJitFortMemInstalled(
codeDesc_.isHugeObj ? nullptr : hostThread_, machineCodeObj.GetObject<MachineCode>());
} else {
auto machineCode = machineCodeObj.GetObject<MachineCode>();
Heap *heap = this->GetHostThread()->GetEcmaVM()->GetHeap();
auto *jitfort = heap->GetOrCreateJitFort();
ASSERT(jitfort != nullptr);
ASSERT(codeDesc_.isHugeObj == jitfort->InHugeRange(machineCode->GetText()));
jitfort->MarkJitFortMemInstalled(machineCode, codeDesc_.isHugeObj);
}
}
if (compilerTier_.IsFastJit()) {
jsFunction_->SetJitCompilingFlag(false);
} else {
ASSERT(compilerTier_.IsBaseLine());
jsFunction_->SetBaselinejitCompilingFlag(false);
}
}
void JitTask::InstallCodeByCompilerTier(JSHandle<MachineCode> &machineCodeObj,
JSHandle<Method> &methodHandle)
{
uintptr_t codeAddr = machineCodeObj->GetFuncAddr();
if (compilerTier_.IsFastJit()) {
jsFunction_->SetCompiledFuncEntry(codeAddr, machineCodeObj->GetIsFastCall());
methodHandle->SetDeoptThreshold(hostThread_->GetEcmaVM()->GetJSOptions().GetDeoptThreshold());
jsFunction_->SetMachineCode(hostThread_, machineCodeObj);
jsFunction_->SetJitMachineCodeCache(hostThread_, machineCodeObj);
uintptr_t codeAddrEnd = codeAddr + machineCodeObj->GetInstructionsSize();
LOG_JIT(INFO) << "Install fast jit machine code, method name: " << GetMethodName()
<< ", function addr: 0x" << std::hex << jsFunction_.GetTaggedType()
<< ", machine code addr: 0x" << machineCodeObj.GetTaggedType()
<< ", code range: " << reinterpret_cast<void*>(codeAddr)
<< "--" << reinterpret_cast<void*>(codeAddrEnd);
#if ECMASCRIPT_ENABLE_JIT_WARMUP_PROFILER
auto &profMap = JitWarmupProfiler::GetInstance()->profMap_;
if (profMap.find(GetMethodName()) != profMap.end()) {
profMap[GetMethodName()] = true;
}
#endif
} else {
ASSERT(compilerTier_.IsBaseLine());
methodHandle->SetDeoptThreshold(hostThread_->GetEcmaVM()->GetJSOptions().GetDeoptThreshold());
jsFunction_->SetBaselineCode(hostThread_, machineCodeObj);
LOG_BASELINEJIT(DEBUG) <<"Install baseline jit machine code:" << GetMethodName();
}
}
void JitTask::SustainingJSHandles()
{
JSHandle<JSFunction> sustainingJsFunctionHandle = sustainingJSHandle_->NewHandle(jsFunction_);
SetJsFunction(sustainingJsFunctionHandle);
JSHandle<ProfileTypeInfo> profileTypeInfo = sustainingJSHandle_->NewHandle(profileTypeInfo_);
SetProfileTypeInfo(profileTypeInfo);
}
void JitTask::ReleaseSustainingJSHandle()
{
sustainingJSHandle_ = nullptr;
}
void JitTask::CloneProfileTypeInfo()
{
Method *method = Method::Cast(jsFunction_->GetMethod(hostThread_).GetTaggedObject());
uint32_t slotSize = method->GetSlotSize();
JSTaggedValue profileTypeInfoVal = jsFunction_->GetProfileTypeInfo(hostThread_);
JSHandle<ProfileTypeInfo> newProfileTypeInfo;
ObjectFactory *factory = hostThread_->GetEcmaVM()->GetFactory();
if (profileTypeInfoVal.IsUndefined() || slotSize == 0) {
slotSize = slotSize == 0 ? 1 : slotSize;
newProfileTypeInfo = factory->NewProfileTypeInfo(slotSize);
} else {
JSHandle<ProfileTypeInfo> profileTypeInfo(hostThread_,
ProfileTypeInfo::Cast(profileTypeInfoVal.GetTaggedObject()));
newProfileTypeInfo = factory->NewProfileTypeInfo(slotSize);
for (uint32_t i = 0; i < slotSize; i++) {
JSTaggedValue value = profileTypeInfo->Get(hostThread_, i);
newProfileTypeInfo->Set(hostThread_, i, value);
}
}
SetProfileTypeInfo(newProfileTypeInfo);
}
JitTask::~JitTask()
{
ReleaseSustainingJSHandle();
jit_->DeleteJitCompilerTask(compilerTask_);
jit_->DecJitTaskCnt(hostThread_);
ASSERT(dependencies_ != nullptr);
delete dependencies_;
dependencies_ = nullptr;
}
void JitTask::WaitFinish()
{
LockHolder lock(runStateMutex_);
if (!IsFinish()) {
runStateCondition_.Wait(&runStateMutex_);
}
}
bool JitTask::AsyncTask::Run([[maybe_unused]] uint32_t threadIndex)
{
if (IsTerminate() || !jitTask_->GetHostThread()->GetEcmaVM()->IsInitialized()) {
return false;
}
DISALLOW_HEAP_ACCESS;
CString info = "compile method: " + jitTask_->GetMethodName();
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK,
ConvertToStdString("JIT::Compile:" + info).c_str(), "");
JitAsyncTaskRunScope asyncTaskRunScope(jitTask_.get());
if (jitTask_->GetJsFunction().GetAddress() == 0) {
} else {
Jit::TimeScope scope(jitTask_->GetHostThread()->GetEcmaVM(), info, jitTask_->GetCompilerTier());
jitTask_->Optimize();
jitTask_->Finalize();
if (!jitTask_->IsCompileSuccess()) {
return false;
}
if (jitTask_->IsAsyncTask()) {
jitTask_->jit_->RequestInstallCode(jitTask_);
}
MachineCodeDesc codeDesc = jitTask_->GetMachineCodeDesc();
size_t instrSize = codeDesc.codeSizeAlign + codeDesc.rodataSizeBeforeTextAlign
+ codeDesc.rodataSizeAfterTextAlign;
CString sizeInfo = ", text size: ";
sizeInfo.append(std::to_string(instrSize)).append("bytes");
scope.appendMessage(sizeInfo);
int compilerTime = scope.TotalSpentTimeInMicroseconds();
JitDfx::GetInstance()->RecordSpentTimeAndPrintStatsLogInJitThread(compilerTime, jitTask_->methodName_,
jitTask_->compilerTier_.IsBaseLine(), jitTask_->mainThreadCompileTime_);
}
return true;
}
JitAsyncTaskRunScope::JitAsyncTaskRunScope(JitTask *task)
: task_(task),
jitvm_(nullptr)
{
task_->SetRunState(RunState::RUNNING);
JSThread *compilerThread = task_->GetCompilerThread();
ASSERT(compilerThread->IsJitThread());
JitThread *jitThread = static_cast<JitThread*>(compilerThread);
jitvm_ = jitThread->GetJitVM();
jitvm_->SetHostVM(task_->GetHostThread());
jitThread->SetCurrentTask(task_);
}
JitAsyncTaskRunScope::~JitAsyncTaskRunScope()
{
JSThread *compilerThread = task_->GetCompilerThread();
JitThread *jitThread = static_cast<JitThread*>(compilerThread);
jitThread->SetCurrentTask(nullptr);
jitvm_->ReSetHostVM();
task_->SetRunStateFinish();
}
}