Commit 4882bb6a authored by Z Nguyen-Huu's avatar Z Nguyen-Huu Committed by Commit Bot

Add fast path for Smi and non-decimal in NumberPrototypeToString

Just a fast iteration over bytes written in Torque for Smi number and
non-decimal radix, also only for more than one string character result.

Improve following micro-benchmark by ~75%

Before
toHexString
toHexString-Numbers(Score): 7905000

After
toHexString
toHexString-Numbers(Score): 14419000

Bug: v8:10477
Change-Id: I366092d4d70156ad33830352c1122af8794bea76
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2330221
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69272}
parent 93c10b26
...@@ -604,6 +604,7 @@ extern builtin ToObject(Context, JSAny): JSReceiver; ...@@ -604,6 +604,7 @@ extern builtin ToObject(Context, JSAny): JSReceiver;
extern macro ToObject_Inline(Context, JSAny): JSReceiver; extern macro ToObject_Inline(Context, JSAny): JSReceiver;
extern macro IsNullOrUndefined(Object): bool; extern macro IsNullOrUndefined(Object): bool;
extern macro IsString(HeapObject): bool; extern macro IsString(HeapObject): bool;
extern macro IsSeqOneByteString(HeapObject): bool;
extern transitioning builtin NonPrimitiveToPrimitive_String( extern transitioning builtin NonPrimitiveToPrimitive_String(
Context, JSAny): JSPrimitive; Context, JSAny): JSPrimitive;
extern transitioning builtin NonPrimitiveToPrimitive_Default( extern transitioning builtin NonPrimitiveToPrimitive_Default(
......
...@@ -537,6 +537,11 @@ Cast<FastJSArrayForReadWithNoCustomIteration>(implicit context: Context)( ...@@ -537,6 +537,11 @@ Cast<FastJSArrayForReadWithNoCustomIteration>(implicit context: Context)(
return %RawDownCast<FastJSArrayForReadWithNoCustomIteration>(a); return %RawDownCast<FastJSArrayForReadWithNoCustomIteration>(a);
} }
Cast<SeqOneByteString>(o: HeapObject): SeqOneByteString labels CastError {
if (!IsSeqOneByteString(o)) goto CastError;
return %RawDownCast<SeqOneByteString>(o);
}
Cast<JSReceiver|Null>(o: HeapObject): JSReceiver|Null Cast<JSReceiver|Null>(o: HeapObject): JSReceiver|Null
labels CastError { labels CastError {
typeswitch (o) { typeswitch (o) {
......
...@@ -57,6 +57,12 @@ FromConstexpr<int8, constexpr int31>(i: constexpr int31): int8 { ...@@ -57,6 +57,12 @@ FromConstexpr<int8, constexpr int31>(i: constexpr int31): int8 {
static_assert(-128 <= i && i <= 127); static_assert(-128 <= i && i <= 127);
return %RawDownCast<int8>(i); return %RawDownCast<int8>(i);
} }
FromConstexpr<char8, constexpr int31>(i: constexpr int31): char8 {
const i: int32 = i;
static_assert(i <= 255);
static_assert(0 <= i);
return %RawDownCast<char8>(i);
}
FromConstexpr<Number, constexpr Smi>(s: constexpr Smi): Number { FromConstexpr<Number, constexpr Smi>(s: constexpr Smi): Number {
return SmiConstant(s); return SmiConstant(s);
} }
......
...@@ -61,6 +61,12 @@ transitioning macro ThisNumberValue(implicit context: Context)( ...@@ -61,6 +61,12 @@ transitioning macro ThisNumberValue(implicit context: Context)(
ToThisValue(receiver, PrimitiveType::kNumber, method)); ToThisValue(receiver, PrimitiveType::kNumber, method));
} }
macro ToCharCode(input: int32): char8 {
assert(0 <= input && input < 36);
return input < 10 ? %RawDownCast<char8>(input + kAsciiZero) :
%RawDownCast<char8>(input - 10 + kAsciiLowerCaseA);
}
// https://tc39.github.io/ecma262/#sec-number.prototype.tostring // https://tc39.github.io/ecma262/#sec-number.prototype.tostring
transitioning javascript builtin NumberPrototypeToString( transitioning javascript builtin NumberPrototypeToString(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
...@@ -86,15 +92,48 @@ transitioning javascript builtin NumberPrototypeToString( ...@@ -86,15 +92,48 @@ transitioning javascript builtin NumberPrototypeToString(
// 7. Return the String representation of this Number // 7. Return the String representation of this Number
// value using the radix specified by radixNumber. // value using the radix specified by radixNumber.
// Fast case where the result is a one character string. if (TaggedIsSmi(x)) {
if (TaggedIsPositiveSmi(x) && x < radixNumber) { const isNegative: bool = x < 0;
let charCode = Convert<int32>(UnsafeCast<Smi>(x)); let n: int32 = Convert<int32>(UnsafeCast<Smi>(x));
if (charCode < 10) { if (!isNegative) {
charCode += kAsciiZero; // Fast case where the result is a one character string.
if (x < radixNumber) {
return StringFromSingleCharCode(ToCharCode(n));
}
} else {
assert(isNegative);
if (n == kMinInt32) {
return runtime::DoubleToStringWithRadix(x, radixNumber);
}
n = 0 - n;
}
const radix: int32 = Convert<int32>(radixNumber);
// Calculate length and pre-allocate the result string.
let temp: int32 = n;
let length: int32 = isNegative ? 1 : 0;
while (temp > 0) {
temp = temp / radix;
length = length + 1;
}
assert(length > 0);
const strSeq = UnsafeCast<SeqOneByteString>(
AllocateSeqOneByteString(Unsigned(length)));
let cursor: intptr = Convert<intptr>(length) - 1;
while (n > 0) {
const digit: int32 = n % radix;
n = n / radix;
strSeq.chars[cursor] = ToCharCode(digit);
cursor = cursor - 1;
}
if (isNegative) {
assert(cursor == 0);
// Insert '-' to result.
strSeq.chars[0] = 45;
} else { } else {
charCode = charCode - 10 + kAsciiLowerCaseA; assert(cursor == -1);
} }
return StringFromSingleCharCode(charCode); return strSeq;
} }
if (x == -0) { if (x == -0) {
...@@ -106,6 +145,7 @@ transitioning javascript builtin NumberPrototypeToString( ...@@ -106,6 +145,7 @@ transitioning javascript builtin NumberPrototypeToString(
} else if (x == MINUS_V8_INFINITY) { } else if (x == MINUS_V8_INFINITY) {
return MinusInfinityStringConstant(); return MinusInfinityStringConstant();
} }
return runtime::DoubleToStringWithRadix(x, radixNumber); return runtime::DoubleToStringWithRadix(x, radixNumber);
} }
......
...@@ -5811,6 +5811,15 @@ TNode<BoolT> CodeStubAssembler::IsSequentialStringInstanceType( ...@@ -5811,6 +5811,15 @@ TNode<BoolT> CodeStubAssembler::IsSequentialStringInstanceType(
Int32Constant(kSeqStringTag)); Int32Constant(kSeqStringTag));
} }
TNode<BoolT> CodeStubAssembler::IsSeqOneByteStringInstanceType(
TNode<Int32T> instance_type) {
CSA_ASSERT(this, IsStringInstanceType(instance_type));
return Word32Equal(
Word32And(instance_type,
Int32Constant(kStringRepresentationMask | kStringEncodingMask)),
Int32Constant(kSeqStringTag | kOneByteStringTag));
}
TNode<BoolT> CodeStubAssembler::IsConsStringInstanceType( TNode<BoolT> CodeStubAssembler::IsConsStringInstanceType(
SloppyTNode<Int32T> instance_type) { SloppyTNode<Int32T> instance_type) {
CSA_ASSERT(this, IsStringInstanceType(instance_type)); CSA_ASSERT(this, IsStringInstanceType(instance_type));
...@@ -6082,6 +6091,10 @@ TNode<BoolT> CodeStubAssembler::IsString(SloppyTNode<HeapObject> object) { ...@@ -6082,6 +6091,10 @@ TNode<BoolT> CodeStubAssembler::IsString(SloppyTNode<HeapObject> object) {
return IsStringInstanceType(LoadInstanceType(object)); return IsStringInstanceType(LoadInstanceType(object));
} }
TNode<BoolT> CodeStubAssembler::IsSeqOneByteString(TNode<HeapObject> object) {
return IsSeqOneByteStringInstanceType(LoadInstanceType(object));
}
TNode<BoolT> CodeStubAssembler::IsSymbolInstanceType( TNode<BoolT> CodeStubAssembler::IsSymbolInstanceType(
SloppyTNode<Int32T> instance_type) { SloppyTNode<Int32T> instance_type) {
return InstanceTypeEqual(instance_type, SYMBOL_TYPE); return InstanceTypeEqual(instance_type, SYMBOL_TYPE);
......
...@@ -2465,6 +2465,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -2465,6 +2465,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> IsNullOrUndefined(SloppyTNode<Object> object); TNode<BoolT> IsNullOrUndefined(SloppyTNode<Object> object);
TNode<BoolT> IsNumberDictionary(SloppyTNode<HeapObject> object); TNode<BoolT> IsNumberDictionary(SloppyTNode<HeapObject> object);
TNode<BoolT> IsOneByteStringInstanceType(TNode<Int32T> instance_type); TNode<BoolT> IsOneByteStringInstanceType(TNode<Int32T> instance_type);
TNode<BoolT> IsSeqOneByteStringInstanceType(TNode<Int32T> instance_type);
TNode<BoolT> IsPrimitiveInstanceType(SloppyTNode<Int32T> instance_type); TNode<BoolT> IsPrimitiveInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsPrivateName(SloppyTNode<Symbol> symbol); TNode<BoolT> IsPrivateName(SloppyTNode<Symbol> symbol);
TNode<BoolT> IsPropertyArray(SloppyTNode<HeapObject> object); TNode<BoolT> IsPropertyArray(SloppyTNode<HeapObject> object);
...@@ -2492,6 +2493,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -2492,6 +2493,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> IsSpecialReceiverMap(SloppyTNode<Map> map); TNode<BoolT> IsSpecialReceiverMap(SloppyTNode<Map> map);
TNode<BoolT> IsStringInstanceType(SloppyTNode<Int32T> instance_type); TNode<BoolT> IsStringInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsString(SloppyTNode<HeapObject> object); TNode<BoolT> IsString(SloppyTNode<HeapObject> object);
TNode<BoolT> IsSeqOneByteString(TNode<HeapObject> object);
TNode<BoolT> IsSymbolInstanceType(SloppyTNode<Int32T> instance_type); TNode<BoolT> IsSymbolInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsInternalizedStringInstanceType(TNode<Int32T> instance_type); TNode<BoolT> IsInternalizedStringInstanceType(TNode<Int32T> instance_type);
TNode<BoolT> IsUniqueName(TNode<HeapObject> object); TNode<BoolT> IsUniqueName(TNode<HeapObject> object);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment