From f39e0370f8a9ca8a4919cd66a937665a9f352ab3 Mon Sep 17 00:00:00 2001 From: huangzhenghua Date: Fri, 17 May 2024 23:31:24 +0800 Subject: [PATCH 1/6] optimize napi_call_function issue: https://gitee.com/openharmony/arkui_napi/issues/I9PY8W Signed-off-by: huangzhenghua Change-Id: I74bf70cf608f43c95121fc41c590dc59a33c4475 --- ecmascript/ecma_context.cpp | 4 ++- ecmascript/global_env_constants.cpp | 1 + ecmascript/global_env_constants.h | 1 + ecmascript/jobs/micro_job_queue.cpp | 7 ---- ecmascript/jobs/micro_job_queue.h | 1 - ecmascript/napi/include/jsnapi_expo.h | 1 + ecmascript/napi/jsnapi_expo.cpp | 50 +++++++++++++++++++-------- 7 files changed, 42 insertions(+), 23 deletions(-) diff --git a/ecmascript/ecma_context.cpp b/ecmascript/ecma_context.cpp index 6a5a29464e..1711266ad6 100644 --- a/ecmascript/ecma_context.cpp +++ b/ecmascript/ecma_context.cpp @@ -770,10 +770,12 @@ void EcmaContext::PrintJSErrorInfo(JSThread *thread, const JSHandleHasTerminated())) { return false; } - return job::MicroJobQueue::HasPendingJob(thread_, GetMicroJobQueue()); + TaggedQueue* promiseQueue = TaggedQueue::Cast(GetMicroJobQueue()->GetPromiseJobQueue().GetTaggedObject()); + return !promiseQueue->Empty(); } bool EcmaContext::ExecutePromisePendingJob() diff --git a/ecmascript/global_env_constants.cpp b/ecmascript/global_env_constants.cpp index de00a48e39..251a28e146 100644 --- a/ecmascript/global_env_constants.cpp +++ b/ecmascript/global_env_constants.cpp @@ -268,6 +268,7 @@ void GlobalEnvConstants::InitSharedMiscellanious(JSThread *thread, ObjectFactory // Specials SetConstant(ConstantIndex::UNDEFINED_INDEX, JSTaggedValue::Undefined()); SetConstant(ConstantIndex::NULL_INDEX, JSTaggedValue::Null()); + SetConstant(ConstantIndex::HOLE_INDEX, JSTaggedValue::Hole()); SetConstant(ConstantIndex::TRUE_INDEX, JSTaggedValue::True()); SetConstant(ConstantIndex::FALSE_INDEX, JSTaggedValue::False()); // Emptys diff --git a/ecmascript/global_env_constants.h b/ecmascript/global_env_constants.h index ab6f99df1a..c14a76ffc2 100644 --- a/ecmascript/global_env_constants.h +++ b/ecmascript/global_env_constants.h @@ -600,6 +600,7 @@ class ObjectFactory; #define SHARED_GLOBAL_ENV_CONSTANT_SPECIAL(V) \ V(JSTaggedValue, Undefined, UNDEFINED_INDEX, ecma_roots_special) \ V(JSTaggedValue, Null, NULL_INDEX, ecma_roots_special) \ + V(JSTaggedValue, Hole, HOLE_INDEX, ecma_roots_special) \ V(JSTaggedValue, True, TRUE_INDEX, ecma_roots_special) \ V(JSTaggedValue, False, FALSE_INDEX, ecma_roots_special) \ V(JSTaggedValue, EmptyString, EMPTY_STRING_OBJECT_INDEX, ecma_roots_special) \ diff --git a/ecmascript/jobs/micro_job_queue.cpp b/ecmascript/jobs/micro_job_queue.cpp index 391dde935b..85f91746e8 100644 --- a/ecmascript/jobs/micro_job_queue.cpp +++ b/ecmascript/jobs/micro_job_queue.cpp @@ -126,11 +126,4 @@ void MicroJobQueue::ExecutePendingJob(JSThread *thread, JSHandle } } } - -bool MicroJobQueue::HasPendingJob(JSThread *thread, JSHandle jobQueue) -{ - [[maybe_unused]] EcmaHandleScope handleScope(thread); - JSHandle promiseQueue(thread, jobQueue->GetPromiseJobQueue()); - return !promiseQueue->Empty(); -} } // namespace panda::ecmascript::job diff --git a/ecmascript/jobs/micro_job_queue.h b/ecmascript/jobs/micro_job_queue.h index 35f04c5499..5f483329a6 100644 --- a/ecmascript/jobs/micro_job_queue.h +++ b/ecmascript/jobs/micro_job_queue.h @@ -39,7 +39,6 @@ public: static void EnqueueJob(JSThread *thread, JSHandle jobQueue, QueueType queueType, const JSHandle &job, const JSHandle &argv); static void ExecutePendingJob(JSThread *thread, JSHandle jobQueue); - static bool HasPendingJob(JSThread *thread, JSHandle jobQueue); static constexpr size_t PROMISE_JOB_QUEUE_OFFSET = Record::SIZE; ACCESSORS(PromiseJobQueue, PROMISE_JOB_QUEUE_OFFSET, SCRIPT_JOB_QUEUE_OFFSET); diff --git a/ecmascript/napi/include/jsnapi_expo.h b/ecmascript/napi/include/jsnapi_expo.h index b0787702eb..f744f0a315 100644 --- a/ecmascript/napi/include/jsnapi_expo.h +++ b/ecmascript/napi/include/jsnapi_expo.h @@ -381,6 +381,7 @@ class ECMA_PUBLIC_API JSValueRef { public: static Local Undefined(const EcmaVM *vm); static Local Null(const EcmaVM *vm); + static Local Hole(const EcmaVM *vm); static Local True(const EcmaVM *vm); static Local False(const EcmaVM *vm); diff --git a/ecmascript/napi/jsnapi_expo.cpp b/ecmascript/napi/jsnapi_expo.cpp index 28781e9e48..ce8da44657 100644 --- a/ecmascript/napi/jsnapi_expo.cpp +++ b/ecmascript/napi/jsnapi_expo.cpp @@ -223,6 +223,12 @@ Local JSValueRef::Null(const EcmaVM *vm) vm->GetJSThread()->GlobalConstants()->GetHandledNull()); } +Local JSValueRef::Hole(const EcmaVM *vm) +{ + return JSNApiHelper::ToLocal( + vm->GetJSThread()->GlobalConstants()->GetHandledHole()); +} + Local JSValueRef::True(const EcmaVM *vm) { return JSNApiHelper::ToLocal( @@ -1897,10 +1903,27 @@ LocalScope::LocalScope(const EcmaVM *vm, JSTaggedType value) : thread_(vm->GetJS LocalScope::~LocalScope() { - ecmascript::ThreadManagedScope managedScope(reinterpret_cast(thread_)); auto context = reinterpret_cast(thread_)->GetCurrentEcmaContext(); - CloseLocalScope(context); - ClosePrimitiveScope(context); + context->HandleScopeCountDec(); + context->PrimitiveScopeCountDec(); + context->SetHandleScopeStorageNext(static_cast(prevNext_)); + context->SetPrimitiveScopeStorageNext(static_cast(prevPrimitiveNext_)); + bool handleScopeNeedShrink = (context->GetHandleScopeStorageEnd() != prevEnd_); + bool primitiveScopeNeedShrink = (context->GetPrimitiveScopeStorageEnd() != prevPrimitiveEnd_); + if (LIKELY(!handleScopeNeedShrink && !primitiveScopeNeedShrink)) { + return; + } + { + ecmascript::ThreadManagedScope managedScope(reinterpret_cast(thread_)); + if (handleScopeNeedShrink) { + context->SetHandleScopeStorageEnd(static_cast(prevEnd_)); + context->ShrinkHandleStorage(prevHandleStorageIndex_); + } + if (primitiveScopeNeedShrink) { + context->SetPrimitiveScopeStorageEnd(static_cast(prevPrimitiveEnd_)); + context->ShrinkPrimitiveStorage(prevPrimitiveStorageIndex_); + } + } } void LocalScope::CloseLocalScope(EcmaContext *context) @@ -2962,7 +2985,7 @@ JSValueRef* FunctionRef::CallForNapi(const EcmaVM *vm, JSValueRef *thisObj, JSValueRef *const argv[], // NOLINTNEXTLINE(modernize-avoid-c-arrays) int32_t length) { - CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, *JSValueRef::Undefined(vm)); + CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, *JSValueRef::Hole(vm)); ecmascript::ThreadManagedScope managedScope(thread); JSTaggedValue result; FunctionCallScope callScope(EcmaVM::ConstCast(vm)); @@ -2981,28 +3004,27 @@ JSValueRef* FunctionRef::CallForNapi(const EcmaVM *vm, JSValueRef *thisObj, } EcmaRuntimeCallInfo *info = ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisValue, undefined, length); - RETURN_VALUE_IF_ABRUPT(thread, *JSValueRef::Undefined(vm)); + RETURN_VALUE_IF_ABRUPT(thread, *JSValueRef::Hole(vm)); for (int32_t i = 0; i < length; i++) { - JSTaggedValue arg = - argv[i] == nullptr ? JSTaggedValue::Undefined() : JSNApiHelper::ToJSTaggedValue(argv[i]); - info->SetCallArg(i, arg); + if (argv[i]) { + // NewRuntimeCallInfo has set Undefined defaultly in Argv's slot. + info->SetCallArg(i, JSNApiHelper::ToJSTaggedValue(argv[i])); + } } - if (thread->IsAsmInterpreter()) { - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, reinterpret_cast( - JSHandle(thread, JSTaggedValue::Exception()).GetAddress())); + if (LIKELY(thread->IsAsmInterpreter())) { STACK_LIMIT_CHECK(thread, reinterpret_cast( - JSHandle(thread, JSTaggedValue::Exception()).GetAddress())); + *JSValueRef::Hole(vm))); auto *hclass = func.GetTaggedObject()->GetClass(); if (hclass->IsClassConstructor()) { RETURN_STACK_BEFORE_THROW_IF_ASM(thread); THROW_TYPE_ERROR_AND_RETURN(thread, "class constructor cannot call", - reinterpret_cast(JSHandle(thread, undefined).GetAddress())); + reinterpret_cast(*JSValueRef::Hole(vm))); } result = ecmascript::InterpreterAssembly::Execute(info); } else { result = JSFunction::Call(info); } - RETURN_VALUE_IF_ABRUPT(thread, *JSValueRef::Undefined(vm)); + RETURN_VALUE_IF_ABRUPT(thread, *JSValueRef::Hole(vm)); vm->GetHeap()->ClearKeptObjects(); if (dm->IsMixedDebugEnabled()) { dm->NotifyReturnNative(); From 5b39f18d1c67dfd86bb80dfb814320b17ab799a8 Mon Sep 17 00:00:00 2001 From: huangzhenghua Date: Tue, 21 May 2024 20:38:20 +0800 Subject: [PATCH 2/6] napi-call-function optimize part2 1. Using macros to isolate handle-leak-check tools 2. Simplify the ClearKeptObjects function process Signed-off-by: huangzhenghua Change-Id: I8e64436169b9711e878cf6d27090a3cda284ec0c --- ecmascript/builtins/builtins_weak_ref.cpp | 2 +- ecmascript/ecma_context.cpp | 27 +++++++++- ecmascript/ecma_context.h | 28 ++++++---- ecmascript/js_weak_ref.h | 2 +- ecmascript/mem/heap.cpp | 20 -------- ecmascript/mem/heap.h | 2 - ecmascript/napi/include/jsnapi_expo.h | 4 -- ecmascript/napi/jsnapi_expo.cpp | 62 +++++++++-------------- 8 files changed, 68 insertions(+), 79 deletions(-) diff --git a/ecmascript/builtins/builtins_weak_ref.cpp b/ecmascript/builtins/builtins_weak_ref.cpp index ad4b85a330..ff5701b362 100644 --- a/ecmascript/builtins/builtins_weak_ref.cpp +++ b/ecmascript/builtins/builtins_weak_ref.cpp @@ -42,7 +42,7 @@ JSTaggedValue BuiltinsWeakRef::WeakRefConstructor(EcmaRuntimeCallInfo *argv) JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 4. Perform ! AddToKeptObjects(target). - thread->GetEcmaVM()->GetHeap()->AddToKeptObjects(target); + thread->GetCurrentEcmaContext()->AddToKeptObjects(target); // 5. Set weakRef.[[WeakRefTarget]] to target. // 6. Return weakRef. JSHandle weakRef = JSHandle::Cast(obj); diff --git a/ecmascript/ecma_context.cpp b/ecmascript/ecma_context.cpp index 1711266ad6..e827181db5 100644 --- a/ecmascript/ecma_context.cpp +++ b/ecmascript/ecma_context.cpp @@ -37,6 +37,7 @@ #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/js_function.h" #include "ecmascript/js_thread.h" +#include "ecmascript/linked_hash_table.h" #include "ecmascript/log.h" #include "ecmascript/log_wrapper.h" #include "ecmascript/module/module_path_helper.h" @@ -192,7 +193,10 @@ EcmaContext::~EcmaContext() } handleStorageNodes_.clear(); currentHandleStorageIndex_ = -1; +#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK handleScopeCount_ = 0; + primitiveScopeCount_ = 0; +#endif handleScopeStorageNext_ = handleScopeStorageEnd_ = nullptr; for (auto n : primitiveStorageNodes_) { @@ -200,7 +204,6 @@ EcmaContext::~EcmaContext() } primitiveStorageNodes_.clear(); currentPrimitiveStorageIndex_ = -1; - primitiveScopeCount_ = 0; primitiveScopeStorageNext_ = primitiveScopeStorageEnd_ = nullptr; if (vm_->IsEnableBaselineJit() || vm_->IsEnableFastJit()) { @@ -1200,4 +1203,26 @@ void EcmaContext::RemoveSustainingJSHandle(SustainingJSHandle *sustainingHandle) sustainingJSHandleList_->RemoveSustainingJSHandle(sustainingHandle); } } + +void EcmaContext::ClearKeptObjects() +{ + if (LIKELY(GetGlobalEnv()->GetTaggedWeakRefKeepObjects().IsUndefined())) { + return; + } + GetGlobalEnv()->SetWeakRefKeepObjects(thread_, JSTaggedValue::Undefined()); +} + +void EcmaContext::AddToKeptObjects(JSHandle value) +{ + JSHandle globalEnv = GetGlobalEnv(); + JSHandle linkedSet; + if (globalEnv->GetWeakRefKeepObjects()->IsUndefined()) { + linkedSet = LinkedHashSet::Create(thread_); + } else { + linkedSet = JSHandle(thread_, + LinkedHashSet::Cast(globalEnv->GetWeakRefKeepObjects()->GetTaggedObject())); + } + linkedSet = LinkedHashSet::Add(thread_, linkedSet, value); + globalEnv->SetWeakRefKeepObjects(thread_, linkedSet); +} } // namespace panda::ecmascript diff --git a/ecmascript/ecma_context.h b/ecmascript/ecma_context.h index 111070bdfd..65662dc92d 100644 --- a/ecmascript/ecma_context.h +++ b/ecmascript/ecma_context.h @@ -368,6 +368,7 @@ public: return currentHandleStorageIndex_; } +#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK void HandleScopeCountAdd() { handleScopeCount_++; @@ -378,6 +379,17 @@ public: handleScopeCount_--; } + void PrimitiveScopeCountAdd() + { + primitiveScopeCount_++; + } + + void PrimitiveScopeCountDec() + { + primitiveScopeCount_--; + } +#endif + void SetLastHandleScope(EcmaHandleScope *scope) { lastHandleScope_ = scope; @@ -413,16 +425,6 @@ public: return currentPrimitiveStorageIndex_; } - void PrimitiveScopeCountAdd() - { - primitiveScopeCount_++; - } - - void PrimitiveScopeCountDec() - { - primitiveScopeCount_--; - } - void SetLastPrimitiveScope(EcmaHandleScope *scope) { lastPrimitiveScope_ = scope; @@ -580,6 +582,8 @@ public: void AddSustainingJSHandle(SustainingJSHandle*); void RemoveSustainingJSHandle(SustainingJSHandle*); + void ClearKeptObjects(); + void AddToKeptObjects(JSHandle value); private: void IterateJitMachineCodeCache(const RootVisitor &v); uint32_t GetJitMachineCodeHash(panda_file::File::EntityId id) const @@ -681,7 +685,10 @@ private: JSTaggedType *handleScopeStorageEnd_ {nullptr}; std::vector *> handleStorageNodes_ {}; int32_t currentHandleStorageIndex_ {-1}; +#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK int32_t handleScopeCount_ {0}; + int32_t primitiveScopeCount_ {0}; +#endif EcmaHandleScope *lastHandleScope_ {nullptr}; // PrimitveScope static constexpr int32_t MIN_PRIMITIVE_STORAGE_SIZE = 2; @@ -689,7 +696,6 @@ private: JSTaggedType *primitiveScopeStorageEnd_ {nullptr}; std::vector *> primitiveStorageNodes_ {}; int32_t currentPrimitiveStorageIndex_ {-1}; - int32_t primitiveScopeCount_ {0}; EcmaHandleScope *lastPrimitiveScope_ {nullptr}; // Frame pointer diff --git a/ecmascript/js_weak_ref.h b/ecmascript/js_weak_ref.h index 4d099122af..b935fe68ec 100644 --- a/ecmascript/js_weak_ref.h +++ b/ecmascript/js_weak_ref.h @@ -38,7 +38,7 @@ public: // 3. Return undefined. JSHandle target(thread, weakRef->GetFromWeak()); if (!target->IsUndefined()) { - thread->GetEcmaVM()->GetHeap()->AddToKeptObjects(target); + thread->GetCurrentEcmaContext()->AddToKeptObjects(target); } return target.GetTaggedValue(); } diff --git a/ecmascript/mem/heap.cpp b/ecmascript/mem/heap.cpp index 5877afd9d4..2b023b3d89 100644 --- a/ecmascript/mem/heap.cpp +++ b/ecmascript/mem/heap.cpp @@ -25,7 +25,6 @@ #include "ecmascript/free_object.h" #include "ecmascript/js_finalization_registry.h" #include "ecmascript/js_native_pointer.h" -#include "ecmascript/linked_hash_table.h" #include "ecmascript/mem/assert_scope.h" #include "ecmascript/mem/concurrent_marker.h" #include "ecmascript/mem/concurrent_sweeper.h" @@ -1291,20 +1290,6 @@ void Heap::OnMoveEvent([[maybe_unused]] uintptr_t address, [[maybe_unused]] Tagg #endif } -void Heap::AddToKeptObjects(JSHandle value) const -{ - JSHandle env = ecmaVm_->GetGlobalEnv(); - JSHandle linkedSet; - if (env->GetWeakRefKeepObjects()->IsUndefined()) { - linkedSet = LinkedHashSet::Create(thread_); - } else { - linkedSet = - JSHandle(thread_, LinkedHashSet::Cast(env->GetWeakRefKeepObjects()->GetTaggedObject())); - } - linkedSet = LinkedHashSet::Add(thread_, linkedSet, value); - env->SetWeakRefKeepObjects(thread_, linkedSet); -} - void Heap::AdjustSpaceSizeForAppSpawn() { SetHeapMode(HeapMode::SPAWN); @@ -1344,11 +1329,6 @@ void Heap::ClearAllocationInspectorFromAllSpaces() hugeMachineCodeSpace_->ClearAllocationInspector(); } -void Heap::ClearKeptObjects() const -{ - ecmaVm_->GetGlobalEnv()->SetWeakRefKeepObjects(thread_, JSTaggedValue::Undefined()); -} - void Heap::RecomputeLimits() { double gcSpeed = memController_->CalculateMarkCompactSpeedPerMS(); diff --git a/ecmascript/mem/heap.h b/ecmascript/mem/heap.h index 2dbb8fc1fc..4bf6b9d6b0 100644 --- a/ecmascript/mem/heap.h +++ b/ecmascript/mem/heap.h @@ -1114,8 +1114,6 @@ public: } #endif void OnMoveEvent(uintptr_t address, TaggedObject* forwardAddress, size_t size); - void AddToKeptObjects(JSHandle value) const; - void ClearKeptObjects() const; // add allocationInspector to each space void AddAllocationInspectorToAllSpaces(AllocationInspector *inspector); diff --git a/ecmascript/napi/include/jsnapi_expo.h b/ecmascript/napi/include/jsnapi_expo.h index f744f0a315..f80b0b9e16 100644 --- a/ecmascript/napi/include/jsnapi_expo.h +++ b/ecmascript/napi/include/jsnapi_expo.h @@ -917,10 +917,6 @@ protected: inline LocalScope(const EcmaVM *vm, JSTaggedType value); private: - void OpenLocalScope(EcmaContext *context); - void OpenPrimitiveScope(EcmaContext *context); - void CloseLocalScope(EcmaContext *context); - void ClosePrimitiveScope(EcmaContext *context); void *prevNext_ = nullptr; void *prevEnd_ = nullptr; int prevHandleStorageIndex_ {-1}; diff --git a/ecmascript/napi/jsnapi_expo.cpp b/ecmascript/napi/jsnapi_expo.cpp index ce8da44657..2fce22e8f1 100644 --- a/ecmascript/napi/jsnapi_expo.cpp +++ b/ecmascript/napi/jsnapi_expo.cpp @@ -1513,7 +1513,7 @@ bool PromiseCapabilityRef::Resolve(const EcmaVM *vm, uintptr_t value) thread->GetCurrentEcmaContext()->ExecutePromisePendingJob(); RETURN_VALUE_IF_ABRUPT(thread, false); - vm->GetHeap()->ClearKeptObjects(); + thread->GetCurrentEcmaContext()->ClearKeptObjects(); return true; } @@ -1537,7 +1537,7 @@ bool PromiseCapabilityRef::Resolve(const EcmaVM *vm, Local value) thread->GetCurrentEcmaContext()->ExecutePromisePendingJob(); RETURN_VALUE_IF_ABRUPT(thread, false); - vm->GetHeap()->ClearKeptObjects(); + thread->GetCurrentEcmaContext()->ClearKeptObjects(); return true; } @@ -1562,7 +1562,7 @@ bool PromiseCapabilityRef::Reject(const EcmaVM *vm, uintptr_t reason) thread->GetCurrentEcmaContext()->ExecutePromisePendingJob(); RETURN_VALUE_IF_ABRUPT(thread, false); - vm->GetHeap()->ClearKeptObjects(); + thread->GetCurrentEcmaContext()->ClearKeptObjects(); return true; } @@ -1587,7 +1587,7 @@ bool PromiseCapabilityRef::Reject(const EcmaVM *vm, Local reason) thread->GetCurrentEcmaContext()->ExecutePromisePendingJob(); RETURN_VALUE_IF_ABRUPT(thread, false); - vm->GetHeap()->ClearKeptObjects(); + thread->GetCurrentEcmaContext()->ClearKeptObjects(); return true; } @@ -1871,41 +1871,45 @@ LocalScope::LocalScope(const EcmaVM *vm) : thread_(vm->GetJSThread()) { // Only get handle ptr here. Do not need to swtich state. auto context = reinterpret_cast(thread_)->GetCurrentEcmaContext(); - OpenLocalScope(context); - OpenPrimitiveScope(context); -} - -void LocalScope::OpenLocalScope(EcmaContext *context) -{ prevNext_ = context->GetHandleScopeStorageNext(); prevEnd_ = context->GetHandleScopeStorageEnd(); prevHandleStorageIndex_ = context->GetCurrentHandleStorageIndex(); - context->HandleScopeCountAdd(); -} -void LocalScope::OpenPrimitiveScope(EcmaContext *context) -{ prevPrimitiveNext_ = context->GetPrimitiveScopeStorageNext(); prevPrimitiveEnd_ = context->GetPrimitiveScopeStorageEnd(); prevPrimitiveStorageIndex_ = context->GetCurrentPrimitiveStorageIndex(); +#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK + context->HandleScopeCountAdd(); context->PrimitiveScopeCountAdd(); +#endif } LocalScope::LocalScope(const EcmaVM *vm, JSTaggedType value) : thread_(vm->GetJSThread()) { ecmascript::ThreadManagedScope managedScope(reinterpret_cast(thread_)); - auto context = reinterpret_cast(thread_)->GetCurrentEcmaContext(); // Simply reserve a slot on the handlescope. The escaped handle will still be retained in this slot. ecmascript::EcmaHandleScope::NewHandle(reinterpret_cast(thread_), value); - OpenLocalScope(context); - OpenPrimitiveScope(context); + auto context = reinterpret_cast(thread_)->GetCurrentEcmaContext(); + prevNext_ = context->GetHandleScopeStorageNext(); + prevEnd_ = context->GetHandleScopeStorageEnd(); + prevHandleStorageIndex_ = context->GetCurrentHandleStorageIndex(); + + prevPrimitiveNext_ = context->GetPrimitiveScopeStorageNext(); + prevPrimitiveEnd_ = context->GetPrimitiveScopeStorageEnd(); + prevPrimitiveStorageIndex_ = context->GetCurrentPrimitiveStorageIndex(); +#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK + context->HandleScopeCountAdd(); + context->PrimitiveScopeCountAdd(); +#endif } LocalScope::~LocalScope() { auto context = reinterpret_cast(thread_)->GetCurrentEcmaContext(); +#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK context->HandleScopeCountDec(); context->PrimitiveScopeCountDec(); +#endif context->SetHandleScopeStorageNext(static_cast(prevNext_)); context->SetPrimitiveScopeStorageNext(static_cast(prevPrimitiveNext_)); bool handleScopeNeedShrink = (context->GetHandleScopeStorageEnd() != prevEnd_); @@ -1926,26 +1930,6 @@ LocalScope::~LocalScope() } } -void LocalScope::CloseLocalScope(EcmaContext *context) -{ - context->HandleScopeCountDec(); - context->SetHandleScopeStorageNext(static_cast(prevNext_)); - if (context->GetHandleScopeStorageEnd() != prevEnd_) { - context->SetHandleScopeStorageEnd(static_cast(prevEnd_)); - context->ShrinkHandleStorage(prevHandleStorageIndex_); - } -} - -void LocalScope::ClosePrimitiveScope(EcmaContext *context) -{ - context->PrimitiveScopeCountDec(); - context->SetPrimitiveScopeStorageNext(static_cast(prevPrimitiveNext_)); - if (context->GetPrimitiveScopeStorageEnd() != prevPrimitiveEnd_) { - context->SetPrimitiveScopeStorageEnd(static_cast(prevPrimitiveEnd_)); - context->ShrinkPrimitiveStorage(prevPrimitiveStorageIndex_); - } -} - // ----------------------------------- EscapeLocalScope ------------------------------ EscapeLocalScope::EscapeLocalScope(const EcmaVM *vm) : LocalScope(vm, JSTaggedValue::Undefined().GetRawData()) { @@ -2976,7 +2960,7 @@ Local FunctionRef::Call(const EcmaVM *vm, Local thisObj, RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm)); JSHandle resultValue(thread, result); - vm->GetHeap()->ClearKeptObjects(); + thread->GetCurrentEcmaContext()->ClearKeptObjects(); vm->GetJsDebuggerManager()->NotifyReturnNative(); return scope.Escape(JSNApiHelper::ToLocal(resultValue)); } @@ -3025,7 +3009,7 @@ JSValueRef* FunctionRef::CallForNapi(const EcmaVM *vm, JSValueRef *thisObj, result = JSFunction::Call(info); } RETURN_VALUE_IF_ABRUPT(thread, *JSValueRef::Hole(vm)); - vm->GetHeap()->ClearKeptObjects(); + thread->GetCurrentEcmaContext()->ClearKeptObjects(); if (dm->IsMixedDebugEnabled()) { dm->NotifyReturnNative(); } From 93999305cbca716807555d682f09a20e09cf43d1 Mon Sep 17 00:00:00 2001 From: huangzhenghua Date: Mon, 27 May 2024 10:12:43 +0800 Subject: [PATCH 3/6] optimize napi-call-function part3 1. opti dm 2. opti ThreadStateTransitionScope 3. opti cas 4. opti CheckSwitchDebuggerBCStub 5. opti hasKeptObj Signed-off-by: huangzhenghua Change-Id: Ib016cd9774ea578245119828c7ad0277a2f0f3e9 --- .../checkpoint/thread_state_transition.h | 5 +++- ecmascript/ecma_context.cpp | 2 ++ ecmascript/ecma_context.h | 6 ++++ ecmascript/js_thread.cpp | 30 ++++++++++--------- ecmascript/js_thread.h | 12 ++++---- ecmascript/napi/jsnapi_expo.cpp | 9 ++++-- 6 files changed, 41 insertions(+), 23 deletions(-) diff --git a/ecmascript/checkpoint/thread_state_transition.h b/ecmascript/checkpoint/thread_state_transition.h index 5eca79455a..fbd27d4983 100644 --- a/ecmascript/checkpoint/thread_state_transition.h +++ b/ecmascript/checkpoint/thread_state_transition.h @@ -32,6 +32,7 @@ public: oldState_ = self_->GetState(); if constexpr (std::is_same_v) { if (oldState_ != newState) { + hasSwitchState_ = true; if constexpr (newState == ThreadState::RUNNING) { self_->TransferDaemonThreadToRunning(); } else { @@ -52,6 +53,7 @@ public: vm->IncreaseUpdateThreadStateTransCount(); } #endif + hasSwitchState_ = true; self_->UpdateState(newState); } } @@ -59,7 +61,7 @@ public: ~ThreadStateTransitionScope() { - if (oldState_ != self_->GetState()) { + if (hasSwitchState_) { if constexpr (std::is_same_v) { if (oldState_ == ThreadState::RUNNING) { self_->TransferDaemonThreadToRunning(); @@ -75,6 +77,7 @@ public: private: T* self_; ThreadState oldState_; + bool hasSwitchState_ = false; NO_COPY_SEMANTIC(ThreadStateTransitionScope); }; diff --git a/ecmascript/ecma_context.cpp b/ecmascript/ecma_context.cpp index e827181db5..8c50d465ff 100644 --- a/ecmascript/ecma_context.cpp +++ b/ecmascript/ecma_context.cpp @@ -1210,6 +1210,7 @@ void EcmaContext::ClearKeptObjects() return; } GetGlobalEnv()->SetWeakRefKeepObjects(thread_, JSTaggedValue::Undefined()); + hasKeptObjects_ = false; } void EcmaContext::AddToKeptObjects(JSHandle value) @@ -1224,5 +1225,6 @@ void EcmaContext::AddToKeptObjects(JSHandle value) } linkedSet = LinkedHashSet::Add(thread_, linkedSet, value); globalEnv->SetWeakRefKeepObjects(thread_, linkedSet); + hasKeptObjects_ = true; } } // namespace panda::ecmascript diff --git a/ecmascript/ecma_context.h b/ecmascript/ecma_context.h index 65662dc92d..7cd8eaba9b 100644 --- a/ecmascript/ecma_context.h +++ b/ecmascript/ecma_context.h @@ -584,6 +584,10 @@ public: void RemoveSustainingJSHandle(SustainingJSHandle*); void ClearKeptObjects(); void AddToKeptObjects(JSHandle value); + inline bool HasKeptObjects() + { + return hasKeptObjects_; + } private: void IterateJitMachineCodeCache(const RootVisitor &v); uint32_t GetJitMachineCodeHash(panda_file::File::EntityId id) const @@ -719,6 +723,8 @@ private: static constexpr uint32_t JIT_MACHINE_CODE_CACHE_SIZE = 263; std::array, JIT_MACHINE_CODE_CACHE_SIZE> jitMachineCodeCache_ {}; + bool hasKeptObjects_ = false; + friend class EcmaHandleScope; friend class JSPandaFileExecutor; friend class ObjectFactory; diff --git a/ecmascript/js_thread.cpp b/ecmascript/js_thread.cpp index b5c501fe9f..93c3f24609 100644 --- a/ecmascript/js_thread.cpp +++ b/ecmascript/js_thread.cpp @@ -629,21 +629,23 @@ size_t JSThread::GetBuiltinPrototypeHClassOffset(BuiltinTypeId type, bool isArch void JSThread::CheckSwitchDebuggerBCStub() { auto isDebug = GetEcmaVM()->GetJsDebuggerManager()->IsDebugMode(); - if (isDebug && - glueData_.bcDebuggerStubEntries_.Get(0) == glueData_.bcDebuggerStubEntries_.Get(1)) { - for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) { - auto stubEntry = glueData_.bcStubEntries_.Get(i); - auto debuggerStubEbtry = glueData_.bcDebuggerStubEntries_.Get(i); - glueData_.bcDebuggerStubEntries_.Set(i, stubEntry); - glueData_.bcStubEntries_.Set(i, debuggerStubEbtry); + if (LIKELY(!isDebug)) { + if (glueData_.bcStubEntries_.Get(0) == glueData_.bcStubEntries_.Get(1)) { + for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) { + auto stubEntry = glueData_.bcDebuggerStubEntries_.Get(i); + auto debuggerStubEbtry = glueData_.bcStubEntries_.Get(i); + glueData_.bcStubEntries_.Set(i, stubEntry); + glueData_.bcDebuggerStubEntries_.Set(i, debuggerStubEbtry); + } } - } else if (!isDebug && - glueData_.bcStubEntries_.Get(0) == glueData_.bcStubEntries_.Get(1)) { - for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) { - auto stubEntry = glueData_.bcDebuggerStubEntries_.Get(i); - auto debuggerStubEbtry = glueData_.bcStubEntries_.Get(i); - glueData_.bcStubEntries_.Set(i, stubEntry); - glueData_.bcDebuggerStubEntries_.Set(i, debuggerStubEbtry); + } else { + if (glueData_.bcDebuggerStubEntries_.Get(0) == glueData_.bcDebuggerStubEntries_.Get(1)) { + for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) { + auto stubEntry = glueData_.bcStubEntries_.Get(i); + auto debuggerStubEbtry = glueData_.bcDebuggerStubEntries_.Get(i); + glueData_.bcDebuggerStubEntries_.Set(i, stubEntry); + glueData_.bcStubEntries_.Set(i, debuggerStubEbtry); + } } } } diff --git a/ecmascript/js_thread.h b/ecmascript/js_thread.h index ab6b17ff10..7b838ab255 100644 --- a/ecmascript/js_thread.h +++ b/ecmascript/js_thread.h @@ -848,14 +848,16 @@ public: volatile auto interruptValue = reinterpret_cast *>(&glueData_.interruptVector_); uint64_t oldValue = interruptValue->load(std::memory_order_relaxed); - uint64_t oldValueBeforeCAS; + auto newValue = oldValue; do { - auto newValue = oldValue; T::Set(value, &newValue); - oldValueBeforeCAS = oldValue; - std::atomic_compare_exchange_strong_explicit(interruptValue, &oldValue, newValue, + bool done = std::atomic_compare_exchange_strong_explicit(interruptValue, &oldValue, newValue, std::memory_order_release, std::memory_order_relaxed); - } while (oldValue != oldValueBeforeCAS); + if (LIKELY(done)) { + return; + } + newValue = oldValue; + } while (true); } void InvokeWeakNodeFreeGlobalCallBack(); diff --git a/ecmascript/napi/jsnapi_expo.cpp b/ecmascript/napi/jsnapi_expo.cpp index 2fce22e8f1..0a6d454130 100644 --- a/ecmascript/napi/jsnapi_expo.cpp +++ b/ecmascript/napi/jsnapi_expo.cpp @@ -2977,7 +2977,8 @@ JSValueRef* FunctionRef::CallForNapi(const EcmaVM *vm, JSValueRef *thisObj, { LocalScope scope(vm); ecmascript::tooling::JsDebuggerManager *dm = vm->GetJsDebuggerManager(); - if (dm->IsDebugApp()) { + bool isDebugApp = dm->IsDebugApp(); + if (isDebugApp) { dm->ClearSingleStepper(); } JSTaggedValue func = *reinterpret_cast(this); @@ -3009,8 +3010,10 @@ JSValueRef* FunctionRef::CallForNapi(const EcmaVM *vm, JSValueRef *thisObj, result = JSFunction::Call(info); } RETURN_VALUE_IF_ABRUPT(thread, *JSValueRef::Hole(vm)); - thread->GetCurrentEcmaContext()->ClearKeptObjects(); - if (dm->IsMixedDebugEnabled()) { + if (thread->GetCurrentEcmaContext()->HasKeptObjects()) { + thread->GetCurrentEcmaContext()->ClearKeptObjects(); + } + if (isDebugApp && dm->IsMixedDebugEnabled()) { dm->NotifyReturnNative(); } } From 92068b5989f87ba9ed90b60c72216c59e7908ee9 Mon Sep 17 00:00:00 2001 From: huangzhenghua Date: Thu, 30 May 2024 01:50:43 +0800 Subject: [PATCH 4/6] napi-opti-part4 fix code-check-warning in cas Signed-off-by: huangzhenghua Change-Id: I2ae289afcee64d57d1b5e1af8afbd1fde3c8524d --- ecmascript/js_thread.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/ecmascript/js_thread.h b/ecmascript/js_thread.h index 7b838ab255..d8e1ac8e86 100644 --- a/ecmascript/js_thread.h +++ b/ecmascript/js_thread.h @@ -850,14 +850,10 @@ public: uint64_t oldValue = interruptValue->load(std::memory_order_relaxed); auto newValue = oldValue; do { - T::Set(value, &newValue); - bool done = std::atomic_compare_exchange_strong_explicit(interruptValue, &oldValue, newValue, - std::memory_order_release, std::memory_order_relaxed); - if (LIKELY(done)) { - return; - } newValue = oldValue; - } while (true); + T::Set(value, &newValue); + } while (!std::atomic_compare_exchange_strong_explicit(interruptValue, &oldValue, newValue, + std::memory_order_release, std::memory_order_relaxed)); } void InvokeWeakNodeFreeGlobalCallBack(); From efbff738495bfe1d9e607ba6269ce6ddc1ff011a Mon Sep 17 00:00:00 2001 From: huangzhenghua Date: Fri, 31 May 2024 16:05:22 +0800 Subject: [PATCH 5/6] optimize napi call function part5 fix review comments Signed-off-by: huangzhenghua Change-Id: Ibf50fb06731620238318b5e200576f61f4f3be1a --- ecmascript/checkpoint/thread_state_transition.h | 4 +++- ecmascript/ecma_context.h | 4 ++-- ecmascript/js_thread.h | 3 ++- ecmascript/napi/jsnapi_expo.cpp | 3 +-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ecmascript/checkpoint/thread_state_transition.h b/ecmascript/checkpoint/thread_state_transition.h index fbd27d4983..5dda20eee7 100644 --- a/ecmascript/checkpoint/thread_state_transition.h +++ b/ecmascript/checkpoint/thread_state_transition.h @@ -32,6 +32,7 @@ public: oldState_ = self_->GetState(); if constexpr (std::is_same_v) { if (oldState_ != newState) { + ASSERT(hasSwitchState_ == false); hasSwitchState_ = true; if constexpr (newState == ThreadState::RUNNING) { self_->TransferDaemonThreadToRunning(); @@ -53,6 +54,7 @@ public: vm->IncreaseUpdateThreadStateTransCount(); } #endif + ASSERT(hasSwitchState_ == false); hasSwitchState_ = true; self_->UpdateState(newState); } @@ -77,7 +79,7 @@ public: private: T* self_; ThreadState oldState_; - bool hasSwitchState_ = false; + bool hasSwitchState_ {false}; NO_COPY_SEMANTIC(ThreadStateTransitionScope); }; diff --git a/ecmascript/ecma_context.h b/ecmascript/ecma_context.h index 7cd8eaba9b..6171f1849b 100644 --- a/ecmascript/ecma_context.h +++ b/ecmascript/ecma_context.h @@ -584,7 +584,7 @@ public: void RemoveSustainingJSHandle(SustainingJSHandle*); void ClearKeptObjects(); void AddToKeptObjects(JSHandle value); - inline bool HasKeptObjects() + inline bool HasKeptObjects() const { return hasKeptObjects_; } @@ -723,7 +723,7 @@ private: static constexpr uint32_t JIT_MACHINE_CODE_CACHE_SIZE = 263; std::array, JIT_MACHINE_CODE_CACHE_SIZE> jitMachineCodeCache_ {}; - bool hasKeptObjects_ = false; + bool hasKeptObjects_ {false}; friend class EcmaHandleScope; friend class JSPandaFileExecutor; diff --git a/ecmascript/js_thread.h b/ecmascript/js_thread.h index d8e1ac8e86..16e2a23d3b 100644 --- a/ecmascript/js_thread.h +++ b/ecmascript/js_thread.h @@ -853,7 +853,8 @@ public: newValue = oldValue; T::Set(value, &newValue); } while (!std::atomic_compare_exchange_strong_explicit(interruptValue, &oldValue, newValue, - std::memory_order_release, std::memory_order_relaxed)); + std::memory_order_release, + std::memory_order_relaxed)); } void InvokeWeakNodeFreeGlobalCallBack(); diff --git a/ecmascript/napi/jsnapi_expo.cpp b/ecmascript/napi/jsnapi_expo.cpp index 0a6d454130..a597f95351 100644 --- a/ecmascript/napi/jsnapi_expo.cpp +++ b/ecmascript/napi/jsnapi_expo.cpp @@ -2997,8 +2997,7 @@ JSValueRef* FunctionRef::CallForNapi(const EcmaVM *vm, JSValueRef *thisObj, } } if (LIKELY(thread->IsAsmInterpreter())) { - STACK_LIMIT_CHECK(thread, reinterpret_cast( - *JSValueRef::Hole(vm))); + STACK_LIMIT_CHECK(thread, reinterpret_cast(*JSValueRef::Hole(vm))); auto *hclass = func.GetTaggedObject()->GetClass(); if (hclass->IsClassConstructor()) { RETURN_STACK_BEFORE_THROW_IF_ASM(thread); From e9020e532a0ea45704e9be26cf6cbf693900fda2 Mon Sep 17 00:00:00 2001 From: huangzhenghua Date: Mon, 3 Jun 2024 20:05:54 +0800 Subject: [PATCH 6/6] fix ut failed Signed-off-by: huangzhenghua Change-Id: I4417f7e8df55a6d66ef7ada6175529ad645195f5 --- ecmascript/tests/glue_regs_test.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ecmascript/tests/glue_regs_test.cpp b/ecmascript/tests/glue_regs_test.cpp index 575f78df38..625de2228c 100644 --- a/ecmascript/tests/glue_regs_test.cpp +++ b/ecmascript/tests/glue_regs_test.cpp @@ -36,9 +36,14 @@ HWTEST_F_L0(GlueRegsTest, ConstantClassTest) ASSERT_NE(globalConst, nullptr); const JSTaggedValue *address = globalConst->BeginSlot(); + size_t curIndex = static_cast(ConstantIndex::CONSTANT_BEGIN); + size_t holeIndex = static_cast(ConstantIndex::HOLE_INDEX); while (address < globalConst->EndSlot()) { - EXPECT_TRUE(!(*address).IsHole()); // Visit barely + if (curIndex != holeIndex) { + EXPECT_TRUE(!(*address).IsHole()); // Visit barely + } address += 1; + curIndex += 1; } }