arkcompiler_ets_runtime/ecmascript/jit/jit_task.cpp
eching b23ea7b9c2 Use GCBitset and Mark Sweep for JitFort GC
Add GCBitset and Mark Sweep changes for JitFort

Issue: IAUVJ6

Signed-off-by: eching <edward.k.ching@gmail.com>
2024-10-18 10:41:39 +08:00

445 lines
18 KiB
C++

/*
* 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/jspandafile/program_object.h"
#include "ecmascript/ohos/jit_tools.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, uint32_t taskThreadId,
JitCompileMode mode)
: hostThread_(hostThread),
compilerThread_(compilerThread),
jit_(jit),
jsFunction_(jsFunction),
compilerTask_(nullptr),
state_(CompileState::SUCCESS),
compilerTier_(tier),
methodName_(methodName),
offset_(offset),
taskThreadId_(taskThreadId),
ecmaContext_(nullptr),
jitCompileMode_(mode),
runState_(RunState::INIT)
{
jit->IncJitTaskCnt(hostThread);
ecmaContext_ = hostThread->GetCurrentEcmaContext();
sustainingJSHandle_ = std::make_unique<SustainingJSHandle>(hostThread->GetEcmaVM());
}
void JitTask::PrepareCompile()
{
CloneProfileTypeInfo();
SustainingJSHandles();
compilerTask_ = jit_->CreateJitCompilerTask(this);
Method *method = Method::Cast(jsFunction_->GetMethod().GetTaggedObject());
JSTaggedValue constpool = method->GetConstantPool();
if (!ConstantPool::CheckUnsharedConstpool(constpool)) {
hostThread_->GetCurrentEcmaContext()->FindOrCreateUnsharedConstpool(constpool);
}
SetRunState(RunState::INIT);
}
void JitTask::Optimize()
{
ECMA_BYTRACE_NAME(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_TAG_ARK, "JIT::Compiler backend");
bool res = jit_->JitFinalize(compilerTask_, this);
if (!res) {
SetCompileFailed();
}
}
void JitTask::InstallOsrCode(JSHandle<MachineCode> &codeObj)
{
auto profile = jsFunction_->GetProfileTypeInfo();
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; // 1 : get last slot
auto profileData = profileInfoHandle->Get(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); // 1 : added for current codeObj
uint32_t i = 0;
for (; i < arr->GetLength(); i++) {
newArr->Set(hostThread_, i, arr->Get(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.rodataSizeBeforeTextAlign = AlignUp(desc.rodataSizeBeforeText, MachineCode::TEXT_ALIGN);
if (desc.codeType == MachineCodeType::BASELINE_CODE) {
desc.codeSizeAlign = Jit::GetInstance()->IsEnableJitFort() ?
AlignUp(desc.codeSize, MachineCode::TEXT_ALIGN) :
AlignUp(desc.codeSize, MachineCode::DATA_ALIGN);
return;
}
// FastJit
if (Jit::GetInstance()->IsEnableJitFort()) {
// align for multiple instruction blocks installed in JitFort
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) {
// only code section in BaselineCode
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) << "InstallCode:: MachineCode Object size to allocate: "
<< allocSize << " (instruction size): " << codeDesc.codeSizeAlign;
if (allocSize > MAX_REGULAR_HEAP_OBJECT_SIZE) {
return payLoadSize;
} else {
// regular sized machine code object instructions are installed in separate jit fort space
return payLoadSize - codeDesc.codeSizeAlign;
}
} else {
return codeDesc.stackMapSizeAlign + codeDesc.codeSizeAlign;
}
}
ASSERT(codeDesc.codeType == MachineCodeType::FAST_JIT_CODE);
if (Jit::GetInstance()->IsEnableJitFort()) {
// instructionsSize: size of JIT generated native instructions
// payLoadSize: size of JIT generated output including native code
size_t instructionsSize =
codeDesc.rodataSizeBeforeTextAlign + codeDesc.codeSizeAlign + codeDesc.rodataSizeAfterTextAlign;
size_t payLoadSize = codeDesc.funcEntryDesSizeAlign + instructionsSize + codeDesc.stackMapSizeAlign;
size_t allocSize = AlignUp(payLoadSize + MachineCode::SIZE,
static_cast<size_t>(MemAlignment::MEM_ALIGN_OBJECT));
LOG_JIT(DEBUG) << "InstallCode:: MachineCode Object size to allocate: "
<< allocSize << " (instruction size): " << instructionsSize;
codeDesc.instructionsSize = instructionsSize;
if (allocSize > MAX_REGULAR_HEAP_OBJECT_SIZE) {
//
// A Huge machine code object is consisted of contiguous 256Kb aligned blocks.
// With JitFort, a huge machine code object starts with a page aligned mutable area
// (that holds Region and MachineCode object header, FuncEntryDesc and StackMap), followed
// by a page aligned nonmutable (JitFort space) area of JIT generated native instructions.
// i.e.
// mutableSize = align up to PageSize
// (sizeof(Region) + HUGE_OBJECT_BITSET_SIZE +MachineCode::SIZE + payLoadSize - instructionsSize)
// immutableSize = instructionsSize (native page boundary aligned)
// See comments at HugeMachineCodeSpace::Allocate()
//
codeDesc.isHugeObj = true;
return payLoadSize;
} else {
// regular sized machine code object instructions are installed in separate jit fort space
return payLoadSize - instructionsSize;
}
} else {
return codeDesc.funcEntryDesSizeAlign + codeDesc.rodataSizeBeforeTextAlign + codeDesc.codeSizeAlign +
codeDesc.rodataSizeAfterTextAlign + codeDesc.stackMapSizeAlign;
}
}
void DumpJitCode(JSHandle<MachineCode> &machineCode, JSHandle<Method> &method)
{
if (!ohos::JitTools::GetJitDumpObjEanble()) {
return;
}
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();
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));
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;
}
int fd = open(outFile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
jitDumpElf.WriteJitElfFile(fd);
close(fd);
}
void JitTask::InstallCode()
{
if (!IsCompileSuccess()) {
return;
}
[[maybe_unused]] EcmaHandleScope handleScope(hostThread_);
JSHandle<Method> methodHandle(hostThread_, Method::Cast(jsFunction_->GetMethod().GetTaggedObject()));
size_t size = ComputePayLoadSize(codeDesc_);
codeDesc_.isAsyncCompileMode = IsAsyncTask();
JSHandle<MachineCode> machineCodeObj;
if (Jit::GetInstance()->IsEnableJitFort()) {
// skip install if JitFort out of memory
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_->ClearException();
}
return;
}
machineCodeObj = hostThread_->GetEcmaVM()->GetFactory()->SetMachineCodeObjectData(
machineCode, size, codeDesc_, methodHandle);
} else {
machineCodeObj = hostThread_->GetEcmaVM()->GetFactory()->NewMachineCodeObject(
size, codeDesc_, methodHandle);
}
if (machineCodeObj.GetAddress() == ToUintPtr(nullptr)) {
// skip install
return;
}
machineCodeObj->SetOSROffset(offset_);
if (hostThread_->HasPendingException()) {
// check is oom exception
hostThread_->SetMachineCodeLowMemory(true);
hostThread_->ClearException();
}
if (IsOsrTask()) {
InstallOsrCode(machineCodeObj);
} else {
InstallCodeByCompilerTier(machineCodeObj, methodHandle);
}
// sometimes get ILL_ILLOPC error if i-cache not flushed for Jit code
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 (!codeDesc_.isHugeObj) {
const Heap *heap = this->GetHostThread()->GetEcmaVM()->GetHeap();
heap->GetMachineCodeSpace()->MarkJitFortMemInstalled(machineCodeObj.GetObject<MachineCode>());
}
}
}
void JitTask::InstallCodeByCompilerTier(JSHandle<MachineCode> &machineCodeObj,
JSHandle<Method> &methodHandle)
{
uintptr_t codeAddr = machineCodeObj->GetFuncAddr();
if (compilerTier_ == CompilerTier::FAST) {
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(DEBUG) <<"Install fast jit machine code:" << GetMethodName() << ", 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_ == CompilerTier::BASELINE);
methodHandle->SetDeoptThreshold(hostThread_->GetEcmaVM()->GetJSOptions().GetDeoptThreshold());
jsFunction_->SetBaselineCode(hostThread_, machineCodeObj);
LOG_BASELINEJIT(DEBUG) <<"Install baseline jit machine code:" << GetMethodName();
}
}
void JitTask::SustainingJSHandles()
{
// transfer to sustaining handle
JSHandle<JSFunction> sustainingJsFunctionHandle = sustainingJSHandle_->NewHandle(jsFunction_);
SetJsFunction(sustainingJsFunctionHandle);
JSHandle<ProfileTypeInfo> profileTypeInfo = sustainingJSHandle_->NewHandle(profileTypeInfo_);
SetProfileTypeInfo(profileTypeInfo);
}
void JitTask::ReleaseSustainingJSHandle()
{
// in abort case, vm exit before task finish, release by explict
sustainingJSHandle_ = nullptr;
}
void JitTask::CloneProfileTypeInfo()
{
[[maybe_unused]] EcmaHandleScope handleScope(hostThread_);
Method *method = Method::Cast(jsFunction_->GetMethod().GetTaggedObject());
uint32_t slotSize = method->GetSlotSize();
JSTaggedValue profileTypeInfoVal = jsFunction_->GetProfileTypeInfo();
JSHandle<ProfileTypeInfo> newProfileTypeInfo;
ObjectFactory *factory = hostThread_->GetEcmaVM()->GetFactory();
if (profileTypeInfoVal.IsUndefined() || slotSize == 0) {
slotSize = slotSize == 0 ? 1 : slotSize; // there's no profiletypeinfo, just generate a temp profiletypeinfo
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(i);
newProfileTypeInfo->Set(hostThread_, i, value);
}
}
SetProfileTypeInfo(newProfileTypeInfo);
}
JitTask::~JitTask()
{
ReleaseSustainingJSHandle();
jit_->DeleteJitCompile(compilerTask_);
jit_->DecJitTaskCnt(hostThread_);
}
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_TAG_ARK, ConvertToStdString("JIT::Compile:" + info));
AsyncTaskRunScope asyncTaskRunScope(jitTask_.get());
if (jitTask_->GetJsFunction().GetAddress() == 0) {
// for unit test
} else {
info = info + ", in jit thread";
Jit::TimeScope scope(jitTask_->GetHostThread()->GetEcmaVM(), info, jitTask_->GetCompilerTier());
jitTask_->Optimize();
jitTask_->Finalize();
// info main thread compile complete
if (!jitTask_->IsCompileSuccess()) {
return false;
}
if (jitTask_->IsAsyncTask()) {
jitTask_->jit_->RequestInstallCode(jitTask_);
}
int compilerTime = scope.TotalSpentTimeInMicroseconds();
JitDfx::GetInstance()->RecordSpentTimeAndPrintStatsLogInJitThread(compilerTime, jitTask_->methodName_,
jitTask_->compilerTier_ == CompilerTier::BASELINE, jitTask_->mainThreadCompileTime_);
}
return true;
}
JitTask::AsyncTask::AsyncTaskRunScope::AsyncTaskRunScope(JitTask *jitTask)
{
jitTask_ = jitTask;
jitTask_->SetRunState(RunState::RUNNING);
JSThread *compilerThread = jitTask_->GetCompilerThread();
ASSERT(compilerThread->IsJitThread());
JitThread *jitThread = static_cast<JitThread*>(compilerThread);
jitvm_ = jitThread->GetJitVM();
jitvm_->SetHostVM(jitTask_->GetHostThread());
}
JitTask::AsyncTask::AsyncTaskRunScope::~AsyncTaskRunScope()
{
jitvm_->ReSetHostVM();
jitTask_->SetRunStateFinish();
}
} // namespace panda::ecmascript