* Copyright (c) 2023 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.
*/
#ifndef ECMASCRIPT_JIT_H
#define ECMASCRIPT_JIT_H
#include "ecmascript/base/config.h"
#include "ecmascript/common.h"
#include "ecmascript/compiler/compilation_env.h"
#include "ecmascript/platform/mutex.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/mem/clock_scope.h"
#include "ecmascript/mem/machine_code.h"
#include "ecmascript/compiler/compiler_log.h"
#include "ecmascript/jit/jit_thread.h"
#include "ecmascript/jit/jit_dfx.h"
#include "ecmascript/jit/compile_decision.h"
#include "ecmascript/jit/jit_resources.h"
namespace panda::ecmascript {
class JitTask;
struct ThreadTaskInfo {
std::deque<std::shared_ptr<JitTask>> installJitTasks_;
bool skipInstallTask_ { false };
std::atomic<uint32_t> jitTaskCnt_;
ConditionVariable jitTaskCntCv_;
};
class Jit {
public:
Jit() {}
~Jit();
static PUBLIC_API Jit *GetInstance();
void SetJitEnablePostFork(EcmaVM *vm, const std::string &bundleName);
void PreFork();
void ConfigJit(EcmaVM *vm);
void SwitchProfileStubs(EcmaVM *vm);
void ConfigOptions(EcmaVM *vm) const;
void ConfigJitFortOptions(EcmaVM *vm);
void SetEnableOrDisable(const JSRuntimeOptions &options, bool isEnableFastJit, bool isEnableBaselineJit);
bool PUBLIC_API IsEnableFastJit() const;
bool PUBLIC_API IsEnableBaselineJit() const;
bool PUBLIC_API IsEnableJitFort() const;
void SetEnableJitFort(bool isEnableJitFort);
bool PUBLIC_API IsDisableCodeSign() const;
void SetDisableCodeSign(bool isEnableCodeSign);
bool PUBLIC_API IsEnableAsyncCopyToFort() const;
void SetEnableAsyncCopyToFort(bool isEnableiAsyncCopyToFort);
static void Compile(EcmaVM *vm, JSHandle<JSFunction> &jsFunction,
CompilerTier::Tier tier = CompilerTier::Tier::FAST,
int32_t offset = MachineCode::INVALID_OSR_OFFSET,
JitCompileMode::Mode mode = JitCompileMode::Mode::SYNC)
{
Compile(vm, jsFunction, CompilerTier(tier), offset, JitCompileMode(mode));
}
bool JitCompile(void *compiler, JitTask *jitTask);
bool JitFinalize(void *compiler, JitTask *jitTask);
void *CreateJitCompilerTask(JitTask *jitTask);
bool IsInitialized() const
{
return initialized_;
}
void DeleteJitCompilerTask(void *compiler);
void RequestInstallCode(std::shared_ptr<JitTask> jitTask);
void InstallTasks(JSThread *jsThread);
void ClearTask(const std::function<bool(common::Task *task)> &checkClear);
void ClearTaskWithVm(EcmaVM *vm);
void Destroy();
uint32_t GetRunningTaskCnt(EcmaVM *vm);
void CheckMechineCodeSpaceMemory(JSThread *thread, int remainSize);
void ChangeTaskPoolState(bool inBackground);
static void CountInterpExecFuncs(JSThread *jsThread, JSHandle<JSFunction> &jsFunction);
bool IsAppJit() const
{
return isApp_;
}
uint32_t GetHotnessThreshold() const
{
return hotnessThreshold_;
}
void SetProfileNeedDump(bool isNeed)
{
isProfileNeedDump_ = isNeed;
}
bool IsProfileNeedDump() const
{
return isProfileNeedDump_;
}
JitDfx *GetJitDfx() const
{
return jitDfx_;
}
void IncJitTaskCnt(JSThread *thread);
void DecJitTaskCnt(JSThread *thread);
NO_COPY_SEMANTIC(Jit);
NO_MOVE_SEMANTIC(Jit);
class TimeScope : public ClockScope {
public:
PUBLIC_API explicit TimeScope(EcmaVM *vm, CString message, CompilerTier tier, bool outPutLog = true,
bool isDebugLevel = false);
explicit TimeScope(EcmaVM *vm)
: vm_(vm), message_(""), tier_(CompilerTier::Tier::FAST), outPutLog_(false), isDebugLevel_(true) {}
PUBLIC_API ~TimeScope();
void appendMessage(const CString& value) { message_ += value; }
private:
EcmaVM *vm_;
CString message_;
CompilerTier tier_;
bool outPutLog_;
bool isDebugLevel_;
};
class JitLockBase {
public:
virtual ~JitLockBase() = default;
protected:
JitLockBase() = default;
NO_COPY_SEMANTIC(JitLockBase);
NO_MOVE_SEMANTIC(JitLockBase);
};
class JitLockHolder : public JitLockBase {
public:
explicit JitLockHolder(JSThread *thread) : thread_(nullptr), scope_(thread->GetEcmaVM())
{
if (thread->IsJitThread()) {
thread_ = static_cast<JitThread*>(thread);
if (!thread_->IsInRunningState()) {
thread_->ManagedCodeBegin();
isInManagedCode_ = true;
}
thread_->GetHostThread()->GetJitLock()->Lock();
auto hostHeap = thread_->GetHostThread()->GetEcmaVM()->GetHeap();
hostHeap->WaitCCFinished();
}
}
explicit JitLockHolder(const CompilationEnv *env, CString message) : thread_(nullptr),
scope_(env->GetJSThread()->GetEcmaVM(),
"Jit Compile Pass: " + message + ", Time:", CompilerTier::Tier::FAST, false)
{
if (env->IsJitCompiler()) {
JSThread *thread = env->GetJSThread();
ASSERT(thread->IsJitThread());
thread_ = static_cast<JitThread*>(thread);
if (!thread_->IsInRunningState()) {
thread_->ManagedCodeBegin();
isInManagedCode_ = true;
}
thread_->GetHostThread()->GetJitLock()->Lock();
auto hostHeap = thread_->GetHostThread()->GetEcmaVM()->GetHeap();
hostHeap->WaitCCFinished();
}
}
~JitLockHolder()
{
if (thread_ != nullptr) {
thread_->GetHostThread()->GetJitLock()->Unlock();
if (isInManagedCode_) {
thread_->ManagedCodeEnd();
}
}
}
JitThread *thread_ {nullptr};
TimeScope scope_;
bool isInManagedCode_{false};
ALLOW_HEAP_ACCESS
NO_COPY_SEMANTIC(JitLockHolder);
NO_MOVE_SEMANTIC(JitLockHolder);
};
class JitGCLockHolder : public JitLockBase {
public:
explicit JitGCLockHolder(JSThread *thread) : thread_(thread)
{
ASSERT(!thread->IsJitThread());
if (Jit::GetInstance()->IsEnableFastJit() || Jit::GetInstance()->IsEnableBaselineJit()) {
LockJit(thread_);
locked_ = true;
}
}
static void LockJit(JSThread *thread)
{
Clock::time_point start = Clock::now();
thread->GetJitLock()->Lock();
Jit::GetInstance()->GetJitDfx()->SetLockHoldingTime(
std::chrono::duration_cast<std::chrono::microseconds>(Clock::now() - start).count());
}
static void UnlockJit(JSThread *thread)
{
thread->GetJitLock()->Unlock();
}
~JitGCLockHolder()
{
if (locked_) {
UnlockJit(thread_);
locked_ = false;
}
}
private:
JSThread *thread_;
bool locked_ { false };
NO_COPY_SEMANTIC(JitGCLockHolder);
NO_MOVE_SEMANTIC(JitGCLockHolder);
};
#if ECMASCRIPT_ENABLE_ARK_STEED
static void CompileArkSteed(EcmaVM *vm, JSHandle<JSFunction> &jsFunction,
CompilerTier tier = CompilerTier::Tier::FAST,
int32_t offset = MachineCode::INVALID_OSR_OFFSET,
JitCompileMode mode = JitCompileMode::Mode::ASYNC);
#endif
private:
void Compile(EcmaVM *vm, const CompileDecision &decision);
static void Compile(EcmaVM *vm, JSHandle<JSFunction> &jsFunction, CompilerTier tier,
int32_t offset, JitCompileMode mode);
#if ECMASCRIPT_ENABLE_ARK_STEED
static bool IsArkSteedBytecodeSupported(EcmaVM *vm, JSHandle<JSFunction> &jsFunction);
#endif
void CreateJitResources();
bool IsLibResourcesResolved() const;
bool initialized_ { false };
bool fastJitEnable_ { false };
bool baselineJitEnable_ { false };
bool isApp_ { false };
bool isProfileNeedDump_ { true };
uint32_t hotnessThreshold_ { 0 };
std::string bundleName_;
std::unordered_map<JSThread*, ThreadTaskInfo> threadTaskInfo_;
RecursiveMutex threadTaskInfoLock_;
bool isEnableJitFort_ { true };
bool isDisableCodeSign_ { true };
bool isEnableAsyncCopyToFort_ { true };
Mutex setEnableLock_;
JitDfx *jitDfx_ { nullptr };
std::unique_ptr<JitResources> jitResources_;
static constexpr int MIN_CODE_SPACE_SIZE = 1_KB;
};
}
#endif