diff --git a/ecmascript/builtins/builtins_number.cpp b/ecmascript/builtins/builtins_number.cpp index bbd99fcfc828922061c92d07ea982bde7bef60cf..e18c2c26a3e36c775e3acdfd70ffa7da8be75a0b 100644 --- a/ecmascript/builtins/builtins_number.cpp +++ b/ecmascript/builtins/builtins_number.cpp @@ -430,8 +430,19 @@ JSTaggedValue BuiltinsNumber::ToString(EcmaRuntimeCallInfo *argv) THROW_RANGE_ERROR_AND_RETURN(thread, "radix must be 2 to 36", JSTaggedValue::Exception()); } // 8. If radixNumber = 10, return ToString(x). + JSHandle cacheTable(thread->GetCurrentEcmaContext()->GetNumberToStringResultCache()); + if (!cacheTable->CacheIsGrow()) { + int length = NumberToStringResultCache::GrowCache(thread, cacheTable); + cacheTable->SetCacheLength(thread, length); + } if (radix == base::DECIMAL) { - return value.ToString(thread).GetTaggedValue(); + JSTaggedValue cacheResult = cacheTable->FindCachedResult(value); + if(cacheResult != JSTaggedValue::Undefined()){ + return JSHandle(thread,EcmaString::Cast(cacheResult)).GetTaggedValue(); + } + JSHandle resultJSHandle = value.ToString(thread); + cacheTable->SetCachedResult(thread, value, resultJSHandle); + return resultJSHandle.GetTaggedValue(); } double valueNumber = value.GetNumber(); @@ -479,4 +490,48 @@ JSTaggedNumber BuiltinsNumber::ThisNumberValue(JSThread *thread, EcmaRuntimeCall [[maybe_unused]] EcmaHandleScope handleScope(thread); THROW_TYPE_ERROR_AND_RETURN(thread, "not number type", JSTaggedNumber::Exception()); } + +JSTaggedValue NumberToStringResultCache::CreateCacheTable(const JSThread *thread) +{ + int length = CACHE_TABLE_HEADER_SIZE + INITIAL_CACHE_NUMBER * ENTRY_SIZE; + + auto table = static_cast( + *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined())); + table->SetCacheLength(thread, INITIAL_CACHE_NUMBER); + return JSTaggedValue(table); +} + +bool NumberToStringResultCache::CacheIsGrow() +{ + return GetCacheLength() == DEFAULT_CACHE_NUMBER; +} + +int NumberToStringResultCache::GrowCache(const JSThread *thread, JSHandle cache) +{ + int length = CACHE_TABLE_HEADER_SIZE + DEFAULT_CACHE_NUMBER * ENTRY_SIZE; + auto factory = thread->GetEcmaVM()->GetFactory(); + auto newCache = factory->ExtendArray(JSHandle(cache), length, JSTaggedValue::Undefined()); + thread->GetCurrentEcmaContext()->SetNumberToStringResultCache(newCache.GetTaggedValue()); + return DEFAULT_CACHE_NUMBER; +} + +JSTaggedValue NumberToStringResultCache::FindCachedResult(JSTaggedValue &number) +{ + if (GetCacheLength() < DEFAULT_CACHE_NUMBER) return JSTaggedValue::Undefined(); + int entry = NumberToStringResultCache::GetNumberHash(number); + uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + JSTaggedValue entryNumber = Get(index + NUMBER_INDEX); + if(entryNumber == number) { + return Get(index + RESULT_INDEX); + } + return JSTaggedValue::Undefined(); +} + +void NumberToStringResultCache::SetCachedResult(const JSThread *thread, JSTaggedValue &number ,JSHandle &result) +{ + int entry = NumberToStringResultCache::GetNumberHash(number); + uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + Set(thread, index + NUMBER_INDEX, number); + Set(thread, index + RESULT_INDEX, result.GetTaggedValue()); +} } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_number.h b/ecmascript/builtins/builtins_number.h index fc770f84ed24a71306ebae7e64421655ab070fa0..7198ffb151066ff80bdfb9f55593e74e3725b97b 100644 --- a/ecmascript/builtins/builtins_number.h +++ b/ecmascript/builtins/builtins_number.h @@ -55,5 +55,50 @@ public: private: static JSTaggedNumber ThisNumberValue(JSThread *thread, EcmaRuntimeCallInfo *argv); }; + +class NumberToStringResultCache : public TaggedArray { +public: + + static NumberToStringResultCache *Cast(TaggedObject *object) + { + return reinterpret_cast(object); + } + static JSTaggedValue CreateCacheTable(const JSThread *thread); + JSTaggedValue FindCachedResult(JSTaggedValue &number); + bool CacheIsGrow(); + static int GrowCache(const JSThread *thread, JSHandle cache); + + void SetCachedResult(const JSThread *thread, JSTaggedValue &number ,JSHandle &result); + int GetNumberHash(JSTaggedValue &number) + { + int mask = GetCacheLength() - 1; + int value = 0; + if (number.IsInt()) { + value = number.GetInt(); + } + else{ + int64_t bits = base::bit_cast(number.GetDouble()); + value = static_cast(bits) ^ static_cast(bits >> 32); + } + return value & mask; + } + inline void SetCacheLength(const JSThread *thread, int length) + { + Set(thread, CACHE_LENGTH_INDEX, JSTaggedValue(length)); + } + + inline int GetCacheLength() + { + return Get(CACHE_LENGTH_INDEX).GetInt(); + } +private: + static constexpr int INITIAL_CACHE_NUMBER = 10; + static constexpr int DEFAULT_CACHE_NUMBER = 16384; + static constexpr int CACHE_LENGTH_INDEX = 0; + static constexpr int CACHE_TABLE_HEADER_SIZE = 1; + static constexpr int NUMBER_INDEX = 0; + static constexpr int RESULT_INDEX = 1; + static constexpr int ENTRY_SIZE = 2; +}; } // namespace panda::ecmascript::builtins #endif // ECMASCRIPT_BUILTINS_BUILTINS_NUBMER_H diff --git a/ecmascript/ecma_context.cpp b/ecmascript/ecma_context.cpp index da5521a372542926d1cf0d492df24d8795a63eb3..b821c1effc584abe99882a87f7a7aa50a0ba36be 100644 --- a/ecmascript/ecma_context.cpp +++ b/ecmascript/ecma_context.cpp @@ -19,6 +19,7 @@ #include "ecmascript/builtins/builtins.h" #include "ecmascript/builtins/builtins_global.h" #include "ecmascript/builtins/builtins_regexp.h" +#include "ecmascript/builtins/builtins_number.h" #include "ecmascript/compiler/aot_file/an_file_data_manager.h" #include "ecmascript/compiler/common_stubs.h" #include "ecmascript/ecma_string_table.h" @@ -100,6 +101,7 @@ bool EcmaContext::Initialize() builtins.Initialize(globalEnv, thread_, builtinsLazyEnabled); SetupRegExpResultCache(); + SetupNumberToStringResultCache(); microJobQueue_ = factory_->NewMicroJobQueue().GetTaggedValue(); moduleManager_ = new ModuleManager(vm_); tsManager_ = new TSManager(vm_); @@ -659,6 +661,11 @@ void EcmaContext::SetupRegExpResultCache() regexpCache_ = builtins::RegExpExecResultCache::CreateCacheTable(thread_); } +void EcmaContext::SetupNumberToStringResultCache() +{ + numberToStringResultCache_ = builtins::NumberToStringResultCache::CreateCacheTable(thread_); +} + void EcmaContext::Iterate(const RootVisitor &v, const RootRangeVisitor &rv) { // visit global Constant @@ -666,6 +673,7 @@ void EcmaContext::Iterate(const RootVisitor &v, const RootRangeVisitor &rv) v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&globalEnv_))); v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(®expCache_))); + v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&numberToStringResultCache_))); v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(µJobQueue_))); if (moduleManager_) { moduleManager_->Iterate(v); diff --git a/ecmascript/ecma_context.h b/ecmascript/ecma_context.h index 84ac8c24656e55491ca5ed3566cbc20f1a5d3ba2..bb87d14495e758170c37d5542b6d2d7ee776fa40 100644 --- a/ecmascript/ecma_context.h +++ b/ecmascript/ecma_context.h @@ -44,6 +44,7 @@ class JSPandaFile; class ConstantPool; class JSPromise; class RegExpExecResultCache; +class NumberToStringResultCache; class EcmaHandleScope; enum class PromiseRejectionEvent : uint8_t; @@ -157,6 +158,7 @@ public: hostPromiseRejectionTracker_ = cb; } void SetupRegExpResultCache(); + void SetupNumberToStringResultCache(); JSHandle GetRegExpCache() const { return JSHandle(reinterpret_cast(®expCache_)); @@ -191,6 +193,15 @@ public: { return AllowAtomicWait_; } + JSHandle GetNumberToStringResultCache() const + { + return JSHandle(reinterpret_cast(&numberToStringResultCache_)); + } + + void SetNumberToStringResultCache(JSTaggedValue newCache) + { + numberToStringResultCache_ = newCache; + } JSHandle GetAndClearEcmaUncaughtException() const; JSHandle GetEcmaUncaughtException() const; void EnableUserUncaughtErrorHandler(); @@ -419,6 +430,7 @@ private: // VM execution states. RegExpParserCache *regExpParserCache_ {nullptr}; + JSTaggedValue numberToStringResultCache_ {JSTaggedValue::Hole()}; JSTaggedValue globalEnv_ {JSTaggedValue::Hole()}; JSTaggedValue regexpCache_ {JSTaggedValue::Hole()}; JSTaggedValue microJobQueue_ {JSTaggedValue::Hole()};