Add fastpath Int32ToString, add singleCharTable in vm

Issue:https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I8ID3Q?from=project-issue

Signed-off-by: yaochaonan <yaochaonan@huawei.com>
Change-Id: I950f1a8530b22e2be1357054a8fbfec18a09dae0
This commit is contained in:
yaochaonan 2023-11-21 21:53:13 +08:00
parent 96162a0358
commit 86555cd737
10 changed files with 202 additions and 25 deletions

View File

@ -24,6 +24,7 @@
#include "ecmascript/base/builtins_base.h"
#include "ecmascript/base/string_helper.h"
#include "ecmascript/ecma_string_table.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/object_factory.h"
@ -115,6 +116,57 @@ bool NumberHelper::IsEmptyString(const uint8_t *start, const uint8_t *end)
return !NumberHelper::GotoNonspace(&p, end);
}
/*
* This Function Translate from number 0-9 to number '0'-'9'
* number 10-35 to number 'a'-'z'
*/
uint32_t NumberHelper::ToCharCode(uint32_t number)
{
ASSERT(number < 36); // total number of '0'-'9' + 'a' -'z'
return number < 10 ? (number + 48): // 48 == '0'; 10: '0' - '9';
(number - 10 + 97); // 97 == 'a'; 'a' - 'z'
}
JSTaggedValue NumberHelper::Int32ToString(JSThread *thread, int32_t number, uint32_t radix)
{
bool isNegative = number < 0;
uint32_t n = 0;
if (!isNegative) {
n = static_cast<uint32_t>(number);
if (n < radix) {
if (n == 0) {
return thread->GlobalConstants()->GetHandledZeroString().GetTaggedValue();
}
JSHandle<SingleCharTable> singleCharTable(thread->GetEcmaVM()->GetSingleCharTable());
return singleCharTable->GetStringFromSingleCharTable(ToCharCode(n));
}
} else {
n = static_cast<uint32_t>(-number);
}
uint32_t temp = n;
uint32_t length = isNegative ? 1 : 0;
// calculate length
while (temp > 0) {
temp = temp / radix;
length = length + 1;
}
std::string buf;
buf.resize(length);
uint32_t index = length - 1;
uint32_t digit = 0;
while (n > 0) {
digit = n % radix;
n /= radix;
buf[index] = ToCharCode(digit) + 0X00;
index--;
}
if (isNegative) {
ASSERT(index == 0);
buf[index] = '-';
}
return thread->GetEcmaVM()->GetFactory()->NewFromUtf8(buf).GetTaggedValue();
}
JSTaggedValue NumberHelper::DoubleToString(JSThread *thread, double number, int radix)
{
static constexpr int BUFFER_SIZE = 2240; // 2240: The size of the character array buffer
@ -367,7 +419,11 @@ JSHandle<EcmaString> NumberHelper::NumberToString(const JSThread *thread, JSTagg
ASSERT(number.IsNumber());
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
if (number.IsInt()) {
return factory->NewFromASCII(IntToString(number.GetInt()));
int intVal = number.GetInt();
if (intVal == 0) {
return JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledZeroString());
}
return factory->NewFromASCII(IntToString(intVal));
}
double d = number.GetDouble();

View File

@ -108,6 +108,8 @@ public:
static JSTaggedValue DoubleToString(JSThread *thread, double number, int radix);
static bool IsEmptyString(const uint8_t *start, const uint8_t *end);
static JSHandle<EcmaString> IntToEcmaString(const JSThread *thread, int number);
static uint32_t ToCharCode(uint32_t number);
static JSTaggedValue Int32ToString(JSThread *thread, int32_t number, uint32_t radix);
static JSHandle<EcmaString> NumberToString(const JSThread *thread, JSTaggedValue number);
static double TruncateDouble(double d);
static int64_t DoubleToInt64(double d);

View File

@ -429,7 +429,9 @@ JSTaggedValue BuiltinsNumber::ToString(EcmaRuntimeCallInfo *argv)
double radix = base::DECIMAL;
JSHandle<JSTaggedValue> radixValue = GetCallArg(argv, 0);
// 5. Else let radixNumber be ToInteger(radix).
if (!radixValue->IsUndefined()) {
if (radixValue->IsInt()) {
radix = radixValue->GetInt();
} else if (!radixValue->IsUndefined()) {
JSTaggedNumber radixNumber = JSTaggedValue::ToInteger(thread, radixValue);
// 6. ReturnIfAbrupt(x).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
@ -452,6 +454,10 @@ JSTaggedValue BuiltinsNumber::ToString(EcmaRuntimeCallInfo *argv)
return resultJSHandle.GetTaggedValue();
}
if (value.IsInt()) {
return NumberHelper::Int32ToString(thread, value.GetInt(), radix);
}
double valueNumber = value.GetNumber();
// If x is NaN, return the String "NaN".
if (std::isnan(valueNumber)) {

View File

@ -214,28 +214,101 @@ HWTEST_F_L0(BuiltinsNumberTest, IsNaN)
TestHelper::TearDownFrame(thread, prev);
}
// new Number(123.456).toString(7)
HWTEST_F_L0(BuiltinsNumberTest, ToString)
HWTEST_F_L0(BuiltinsNumberTest, ToString1)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
auto ecmaVM = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();
// new Number(123.456).toString(7)
JSHandle<JSFunction> numberObject(env->GetNumberFunction());
JSHandle<JSTaggedValue> value(thread, JSTaggedValue(123.456));
JSHandle<JSPrimitiveRef> number = thread->GetEcmaVM()->GetFactory()->NewJSPrimitiveRef(numberObject, value);
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue());
ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(7.0));
auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo1->SetThis(number.GetTaggedValue());
ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(7.0));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
JSTaggedValue result = BuiltinsNumber::ToString(ecmaRuntimeCallInfo);
ASSERT_TRUE(result.IsString());
JSHandle<EcmaString> res(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
JSHandle<EcmaString> correctResult = factory->NewFromASCII("234.312256641535441");
ASSERT_TRUE(EcmaStringAccessor::StringsAreEqual(*res, *correctResult));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
JSTaggedValue result1 = BuiltinsNumber::ToString(ecmaRuntimeCallInfo1);
ASSERT_TRUE(result1.IsString());
JSHandle<EcmaString> res1(thread, reinterpret_cast<EcmaString *>(result1.GetRawData()));
JSHandle<EcmaString> correctResult1 = factory->NewFromASCII("234.312256641535441");
ASSERT_TRUE(EcmaStringAccessor::StringsAreEqual(*res1, *correctResult1));
TestHelper::TearDownFrame(thread, prev);
// (15).toString(4)
auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo2->SetThis(JSTaggedValue(15));
ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(4));
prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2);
JSTaggedValue result2 = BuiltinsNumber::ToString(ecmaRuntimeCallInfo2);
ASSERT_TRUE(result2.IsString());
JSHandle<EcmaString> res2(thread, reinterpret_cast<EcmaString *>(result2.GetRawData()));
JSHandle<EcmaString> correctResult2 = factory->NewFromASCII("33");
ASSERT_TRUE(EcmaStringAccessor::StringsAreEqual(*res2, *correctResult2));
TestHelper::TearDownFrame(thread, prev);
// (5).toString(8)
auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo3->SetThis(JSTaggedValue(5));
ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(8));
prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3);
JSTaggedValue result3 = BuiltinsNumber::ToString(ecmaRuntimeCallInfo3);
ASSERT_TRUE(result3.IsString());
JSHandle<EcmaString> res3(thread, reinterpret_cast<EcmaString *>(result3.GetRawData()));
JSHandle<EcmaString> correctResult3 = factory->NewFromASCII("5");
ASSERT_TRUE(EcmaStringAccessor::StringsAreEqual(*res3, *correctResult3));
TestHelper::TearDownFrame(thread, prev);
// (0).toString(8)
auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo4->SetThis(JSTaggedValue(0));
ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(8));
prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4);
JSTaggedValue result4 = BuiltinsNumber::ToString(ecmaRuntimeCallInfo4);
ASSERT_TRUE(result4.IsString());
JSHandle<EcmaString> res4(thread, reinterpret_cast<EcmaString *>(result4.GetRawData()));
JSHandle<EcmaString> correctResult4 = factory->NewFromASCII("0");
ASSERT_TRUE(EcmaStringAccessor::StringsAreEqual(*res4, *correctResult4));
TestHelper::TearDownFrame(thread, prev);
}
HWTEST_F_L0(BuiltinsNumberTest, ToString2)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
// (-50).toString(35)
auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo1->SetThis(JSTaggedValue(-50));
ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(35));
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
JSTaggedValue result1 = BuiltinsNumber::ToString(ecmaRuntimeCallInfo1);
ASSERT_TRUE(result1.IsString());
JSHandle<EcmaString> res1(thread, reinterpret_cast<EcmaString *>(result1.GetRawData()));
JSHandle<EcmaString> correctResult1 = factory->NewFromASCII("-1f");
ASSERT_TRUE(EcmaStringAccessor::StringsAreEqual(*res1, *correctResult1));
TestHelper::TearDownFrame(thread, prev);
// (2).toString(2.5)
auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo2->SetThis(JSTaggedValue(2));
ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(2.5));
prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2);
JSTaggedValue result2 = BuiltinsNumber::ToString(ecmaRuntimeCallInfo2);
ASSERT_TRUE(result2.IsString());
JSHandle<EcmaString> res2(thread, reinterpret_cast<EcmaString *>(result2.GetRawData()));
JSHandle<EcmaString> correctResult2 = factory->NewFromASCII("10");
ASSERT_TRUE(EcmaStringAccessor::StringsAreEqual(*res2, *correctResult2));
TestHelper::TearDownFrame(thread, prev);
}

View File

@ -20,6 +20,7 @@
#include "ecmascript/js_thread.h"
#include "ecmascript/jspandafile/js_pandafile.h"
#include "ecmascript/mem/c_string.h"
#include "ecmascript/mem/space.h"
#include "ecmascript/object_factory.h"
namespace panda::ecmascript {
@ -310,4 +311,18 @@ bool EcmaStringTable::CheckStringTableValidity()
}
return true;
}
JSTaggedValue SingleCharTable::CreateSingleCharTable(const JSThread *thread)
{
auto table = static_cast<SingleCharTable*>(
*thread->GetEcmaVM()->GetFactory()->NewTaggedArray(MAX_ONEBYTE_CHARCODE,
JSTaggedValue::Undefined(), MemSpaceType::NON_MOVABLE));
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
table->Set(thread, 0, thread->GlobalConstants()->GetHandledZeroString().GetTaggedValue());
for (uint32_t i = 1; i < MAX_ONEBYTE_CHARCODE; ++i) {
std::string tmp(1, i + 0X00); // 1: size
table->Set(thread, i, factory->NewFromASCIINonMovable(tmp).GetTaggedValue());
}
return JSTaggedValue(table);
}
} // namespace panda::ecmascript

View File

@ -19,6 +19,7 @@
#include "ecmascript/mem/c_containers.h"
#include "ecmascript/mem/space.h"
#include "ecmascript/mem/visitor.h"
#include "ecmascript/tagged_array-inl.h"
namespace panda::ecmascript {
class EcmaString;
@ -72,6 +73,21 @@ private:
const EcmaVM *vm_{nullptr};
friend class SnapshotProcessor;
};
class SingleCharTable : public TaggedArray {
public:
static SingleCharTable *Cast(TaggedObject *object)
{
return reinterpret_cast<SingleCharTable*>(object);
}
static JSTaggedValue CreateSingleCharTable(const JSThread *thread);
JSTaggedValue GetStringFromSingleCharTable(int32_t ch)
{
return Get(ch);
}
private:
static constexpr uint32_t MAX_ONEBYTE_CHARCODE = 128; // 0X00-0X7F
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_STRING_TABLE_H

View File

@ -232,6 +232,7 @@ bool EcmaVM::Initialize()
thread_->GetCurrentEcmaContext()->LoadStubFile();
}
singleCharTable_ = SingleCharTable::CreateSingleCharTable(thread_);
callTimer_ = new FunctionCallTimer();
strategy_ = new ThroughputJSObjectResizingStrategy();
initialized_ = true;
@ -507,6 +508,7 @@ void EcmaVM::Iterate(const RootVisitor &v, const RootRangeVisitor &rv)
{
rv(Root::ROOT_VM, ObjectSlot(ToUintPtr(&internalNativeMethods_.front())),
ObjectSlot(ToUintPtr(&internalNativeMethods_.back()) + JSTaggedValue::TaggedTypeSize()));
v(Root::ROOT_VM, ObjectSlot(ToUintPtr(&singleCharTable_)));
if (!WIN_OR_MAC_OR_IOS_PLATFORM) {
snapshotEnv_->Iterate(v);
}

View File

@ -54,6 +54,7 @@ enum class PromiseRejectionEvent : uint8_t;
class JSPandaFileManager;
class JSPandaFile;
class EcmaStringTable;
class SingleCharTable;
class SnapshotEnv;
class SnapshotSerialize;
class SnapshotProcessor;
@ -450,6 +451,12 @@ public:
return stringTable_;
}
JSHandle<JSTaggedValue> GetSingleCharTable() const
{
ASSERT(singleCharTable_ != JSTaggedValue::Hole());
return JSHandle<JSTaggedValue>(reinterpret_cast<uintptr_t>(&singleCharTable_));
}
void IncreaseCallDepth()
{
callDepth_++;
@ -501,6 +508,7 @@ private:
bool initialized_ {false};
GCStats *gcStats_ {nullptr};
EcmaStringTable *stringTable_;
JSTaggedValue singleCharTable_ {JSTaggedValue::Hole()};
// VM memory management.
std::unique_ptr<NativeAreaAllocator> nativeAreaAllocator_;

View File

@ -70,22 +70,20 @@ std::enable_if_t<std::is_floating_point_v<T>, CString> FloatToCString(T number)
template<class T>
std::enable_if_t<std::is_integral_v<T>, CString> ToCString(T number)
{
if (number == 0) {
return CString("0");
}
static constexpr uint32_t BUFF_SIZE = std::numeric_limits<T>::digits10 + 3; // 3: Reserved for sign bit and '\0'.
int64_t n = static_cast<int64_t>(number);
char buf[BUFF_SIZE];
uint32_t position = BUFF_SIZE - 1;
buf[position] = '\0';
bool isNeg = number < 0;
while (number != 0) {
if (isNeg) {
buf[--position] = static_cast<int8_t>('0' - (number % 10)); // 10 : decimal
} else {
buf[--position] = static_cast<int8_t>('0' + (number % 10)); // 10 : decimal
}
number /= 10; // 10 : decimal
bool isNeg = true;
if (n >= 0) {
n = -n;
isNeg = false;
}
do {
buf[--position] = static_cast<int8_t>('0' - (n % 10)); // 10 : decimal
n /= 10; // 10 : decimal
} while (n);
if (isNeg) {
buf[--position] = '-';
}

View File

@ -655,6 +655,7 @@ private:
friend class EcmaString;
friend class SnapshotProcessor;
friend class TSManager;
friend class SingleCharTable;
void InitObjectFields(const TaggedObject *object);
JSThread *thread_ {nullptr};