[csa] Add ToLength and ToString variants with inlined fast checks
Smis can easily be handled outside the stub call without adding much to code size. The ToString inlining adds overhead of repeated instance type loads and checks, but under the assumption that it is called with mostly string values it should speed things up (a local RegExp.p[@@replace] microbenchmark shows consistent 1.6% improvements). Drive-by-fix: Remove duplication in ToString implementations. BUG= Review-Url: https://codereview.chromium.org/2874423003 Cr-Commit-Position: refs/heads/master@{#45287}
This commit is contained in:
parent
3a80814d53
commit
646fdacaa7
@ -303,8 +303,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
|
|||||||
BIND(¬_js_array);
|
BIND(¬_js_array);
|
||||||
Node* len_property =
|
Node* len_property =
|
||||||
GetProperty(context(), o(), isolate()->factory()->length_string());
|
GetProperty(context(), o(), isolate()->factory()->length_string());
|
||||||
merged_length.Bind(
|
merged_length.Bind(ToLength_Inline(context(), len_property));
|
||||||
CallStub(CodeFactory::ToLength(isolate()), context(), len_property));
|
|
||||||
Goto(&has_length);
|
Goto(&has_length);
|
||||||
BIND(&has_length);
|
BIND(&has_length);
|
||||||
len_ = merged_length.value();
|
len_ = merged_length.value();
|
||||||
@ -2197,8 +2196,7 @@ TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) {
|
|||||||
{
|
{
|
||||||
Node* length =
|
Node* length =
|
||||||
GetProperty(context, array, factory()->length_string());
|
GetProperty(context, array, factory()->length_string());
|
||||||
Callable to_length = CodeFactory::ToLength(isolate());
|
var_length.Bind(ToLength_Inline(context, length));
|
||||||
var_length.Bind(CallStub(to_length, context, length));
|
|
||||||
Goto(&done);
|
Goto(&done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,35 +133,7 @@ TF_BUILTIN(ToString, CodeStubAssembler) {
|
|||||||
Node* context = Parameter(Descriptor::kContext);
|
Node* context = Parameter(Descriptor::kContext);
|
||||||
Node* input = Parameter(Descriptor::kArgument);
|
Node* input = Parameter(Descriptor::kArgument);
|
||||||
|
|
||||||
Label is_number(this);
|
Return(ToString(context, input));
|
||||||
Label runtime(this);
|
|
||||||
|
|
||||||
GotoIf(TaggedIsSmi(input), &is_number);
|
|
||||||
|
|
||||||
Node* input_map = LoadMap(input);
|
|
||||||
Node* input_instance_type = LoadMapInstanceType(input_map);
|
|
||||||
|
|
||||||
Label not_string(this);
|
|
||||||
GotoIfNot(IsStringInstanceType(input_instance_type), ¬_string);
|
|
||||||
Return(input);
|
|
||||||
|
|
||||||
Label not_heap_number(this);
|
|
||||||
|
|
||||||
BIND(¬_string);
|
|
||||||
{ Branch(IsHeapNumberMap(input_map), &is_number, ¬_heap_number); }
|
|
||||||
|
|
||||||
BIND(&is_number);
|
|
||||||
{ Return(NumberToString(context, input)); }
|
|
||||||
|
|
||||||
BIND(¬_heap_number);
|
|
||||||
{
|
|
||||||
GotoIf(Word32NotEqual(input_instance_type, Int32Constant(ODDBALL_TYPE)),
|
|
||||||
&runtime);
|
|
||||||
Return(LoadObjectField(input, Oddball::kToStringOffset));
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&runtime);
|
|
||||||
{ Return(CallRuntime(Runtime::kToString, context, input)); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7.1.1.1 OrdinaryToPrimitive ( O, hint )
|
// 7.1.1.1 OrdinaryToPrimitive ( O, hint )
|
||||||
|
@ -577,8 +577,7 @@ Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
|
|||||||
|
|
||||||
BIND(&call_tolength);
|
BIND(&call_tolength);
|
||||||
{
|
{
|
||||||
var_lastindex.Bind(
|
var_lastindex.Bind(ToLength_Inline(context, regexp_lastindex));
|
||||||
CallBuiltin(Builtins::kToLength, context, regexp_lastindex));
|
|
||||||
Goto(&next);
|
Goto(&next);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1941,7 +1940,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context,
|
|||||||
if (is_fastpath) {
|
if (is_fastpath) {
|
||||||
CSA_ASSERT(this, TaggedIsPositiveSmi(last_index));
|
CSA_ASSERT(this, TaggedIsPositiveSmi(last_index));
|
||||||
} else {
|
} else {
|
||||||
last_index = CallBuiltin(Builtins::kToLength, context, last_index);
|
last_index = ToLength_Inline(context, last_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* const new_last_index =
|
Node* const new_last_index =
|
||||||
@ -2809,8 +2808,7 @@ TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
|
|||||||
// 3. Does ToString({replace_value}) contain '$'?
|
// 3. Does ToString({replace_value}) contain '$'?
|
||||||
BIND(&checkreplacestring);
|
BIND(&checkreplacestring);
|
||||||
{
|
{
|
||||||
Node* const replace_string =
|
Node* const replace_string = ToString_Inline(context, replace_value);
|
||||||
CallBuiltin(Builtins::kToString, context, replace_value);
|
|
||||||
|
|
||||||
// ToString(replaceValue) could potentially change the shape of the RegExp
|
// ToString(replaceValue) could potentially change the shape of the RegExp
|
||||||
// object. Recheck that we are still on the fast path and bail to runtime
|
// object. Recheck that we are still on the fast path and bail to runtime
|
||||||
@ -2898,7 +2896,7 @@ TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
|
|||||||
Node* const receiver = maybe_receiver;
|
Node* const receiver = maybe_receiver;
|
||||||
|
|
||||||
// Convert {maybe_string} to a String.
|
// Convert {maybe_string} to a String.
|
||||||
Node* const string = CallBuiltin(Builtins::kToString, context, maybe_string);
|
Node* const string = ToString_Inline(context, maybe_string);
|
||||||
|
|
||||||
// Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
|
// Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
|
||||||
Label stub(this), runtime(this, Label::kDeferred);
|
Label stub(this), runtime(this, Label::kDeferred);
|
||||||
|
@ -767,7 +767,7 @@ TF_BUILTIN(StringPrototypeConcat, CodeStubAssembler) {
|
|||||||
arguments.ForEach(
|
arguments.ForEach(
|
||||||
CodeStubAssembler::VariableList({&var_result}, zone()),
|
CodeStubAssembler::VariableList({&var_result}, zone()),
|
||||||
[this, context, &var_result](Node* arg) {
|
[this, context, &var_result](Node* arg) {
|
||||||
arg = CallStub(CodeFactory::ToString(isolate()), context, arg);
|
arg = ToString_Inline(context, arg);
|
||||||
var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context,
|
var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context,
|
||||||
var_result.value(), arg));
|
var_result.value(), arg));
|
||||||
});
|
});
|
||||||
@ -1184,9 +1184,7 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
|
|||||||
MaybeCallFunctionAtSymbol(
|
MaybeCallFunctionAtSymbol(
|
||||||
context, search, isolate()->factory()->replace_symbol(),
|
context, search, isolate()->factory()->replace_symbol(),
|
||||||
[=]() {
|
[=]() {
|
||||||
Callable tostring_callable = CodeFactory::ToString(isolate());
|
Node* const subject_string = ToString_Inline(context, receiver);
|
||||||
Node* const subject_string =
|
|
||||||
CallStub(tostring_callable, context, receiver);
|
|
||||||
|
|
||||||
Callable replace_callable = CodeFactory::RegExpReplace(isolate());
|
Callable replace_callable = CodeFactory::RegExpReplace(isolate());
|
||||||
return CallStub(replace_callable, context, search, subject_string,
|
return CallStub(replace_callable, context, search, subject_string,
|
||||||
@ -1199,11 +1197,10 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
|
|||||||
|
|
||||||
// Convert {receiver} and {search} to strings.
|
// Convert {receiver} and {search} to strings.
|
||||||
|
|
||||||
Callable tostring_callable = CodeFactory::ToString(isolate());
|
|
||||||
Callable indexof_callable = CodeFactory::StringIndexOf(isolate());
|
Callable indexof_callable = CodeFactory::StringIndexOf(isolate());
|
||||||
|
|
||||||
Node* const subject_string = CallStub(tostring_callable, context, receiver);
|
Node* const subject_string = ToString_Inline(context, receiver);
|
||||||
Node* const search_string = CallStub(tostring_callable, context, search);
|
Node* const search_string = ToString_Inline(context, search);
|
||||||
|
|
||||||
Node* const subject_length = LoadStringLength(subject_string);
|
Node* const subject_length = LoadStringLength(subject_string);
|
||||||
Node* const search_length = LoadStringLength(search_string);
|
Node* const search_length = LoadStringLength(search_string);
|
||||||
@ -1257,7 +1254,7 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
|
|||||||
|
|
||||||
// TODO(jgruber): Could introduce ToStringSideeffectsStub which only
|
// TODO(jgruber): Could introduce ToStringSideeffectsStub which only
|
||||||
// performs observable parts of ToString.
|
// performs observable parts of ToString.
|
||||||
CallStub(tostring_callable, context, replace);
|
ToString_Inline(context, replace);
|
||||||
Goto(&return_subject);
|
Goto(&return_subject);
|
||||||
|
|
||||||
BIND(&return_subject);
|
BIND(&return_subject);
|
||||||
@ -1300,8 +1297,7 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
|
|||||||
Node* const replacement =
|
Node* const replacement =
|
||||||
CallJS(call_callable, context, replace, UndefinedConstant(),
|
CallJS(call_callable, context, replace, UndefinedConstant(),
|
||||||
search_string, match_start_index, subject_string);
|
search_string, match_start_index, subject_string);
|
||||||
Node* const replacement_string =
|
Node* const replacement_string = ToString_Inline(context, replacement);
|
||||||
CallStub(tostring_callable, context, replacement);
|
|
||||||
var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
|
var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
|
||||||
replacement_string));
|
replacement_string));
|
||||||
Goto(&out);
|
Goto(&out);
|
||||||
@ -1309,7 +1305,7 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
|
|||||||
|
|
||||||
BIND(&if_notcallablereplace);
|
BIND(&if_notcallablereplace);
|
||||||
{
|
{
|
||||||
Node* const replace_string = CallStub(tostring_callable, context, replace);
|
Node* const replace_string = ToString_Inline(context, replace);
|
||||||
Node* const replacement =
|
Node* const replacement =
|
||||||
GetSubstitution(context, subject_string, match_start_index,
|
GetSubstitution(context, subject_string, match_start_index,
|
||||||
match_end_index, replace_string);
|
match_end_index, replace_string);
|
||||||
@ -1429,9 +1425,7 @@ TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
|
|||||||
MaybeCallFunctionAtSymbol(
|
MaybeCallFunctionAtSymbol(
|
||||||
context, separator, isolate()->factory()->split_symbol(),
|
context, separator, isolate()->factory()->split_symbol(),
|
||||||
[=]() {
|
[=]() {
|
||||||
Callable tostring_callable = CodeFactory::ToString(isolate());
|
Node* const subject_string = ToString_Inline(context, receiver);
|
||||||
Node* const subject_string =
|
|
||||||
CallStub(tostring_callable, context, receiver);
|
|
||||||
|
|
||||||
Callable split_callable = CodeFactory::RegExpSplit(isolate());
|
Callable split_callable = CodeFactory::RegExpSplit(isolate());
|
||||||
return CallStub(split_callable, context, separator, subject_string,
|
return CallStub(split_callable, context, separator, subject_string,
|
||||||
@ -1447,14 +1441,12 @@ TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
|
|||||||
// but AFAIK there should not be a difference since arrays are capped at Smi
|
// but AFAIK there should not be a difference since arrays are capped at Smi
|
||||||
// lengths.
|
// lengths.
|
||||||
|
|
||||||
Callable tostring_callable = CodeFactory::ToString(isolate());
|
Node* const subject_string = ToString_Inline(context, receiver);
|
||||||
Node* const subject_string = CallStub(tostring_callable, context, receiver);
|
|
||||||
Node* const limit_number =
|
Node* const limit_number =
|
||||||
Select(IsUndefined(limit), [=]() { return SmiConstant(Smi::kMaxValue); },
|
Select(IsUndefined(limit), [=]() { return SmiConstant(Smi::kMaxValue); },
|
||||||
[=]() { return ToUint32(context, limit); },
|
[=]() { return ToUint32(context, limit); },
|
||||||
MachineRepresentation::kTagged);
|
MachineRepresentation::kTagged);
|
||||||
Node* const separator_string =
|
Node* const separator_string = ToString_Inline(context, separator);
|
||||||
CallStub(tostring_callable, context, separator);
|
|
||||||
|
|
||||||
// Shortcut for {limit} == 0.
|
// Shortcut for {limit} == 0.
|
||||||
{
|
{
|
||||||
|
@ -4480,6 +4480,22 @@ Node* CodeStubAssembler::ToString(Node* context, Node* input) {
|
|||||||
return result.value();
|
return result.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node* CodeStubAssembler::ToString_Inline(Node* const context,
|
||||||
|
Node* const input) {
|
||||||
|
VARIABLE(var_result, MachineRepresentation::kTagged, input);
|
||||||
|
Label stub_call(this, Label::kDeferred), out(this);
|
||||||
|
|
||||||
|
GotoIf(TaggedIsSmi(input), &stub_call);
|
||||||
|
Branch(IsString(input), &out, &stub_call);
|
||||||
|
|
||||||
|
BIND(&stub_call);
|
||||||
|
var_result.Bind(CallBuiltin(Builtins::kToString, context, input));
|
||||||
|
Goto(&out);
|
||||||
|
|
||||||
|
BIND(&out);
|
||||||
|
return var_result.value();
|
||||||
|
}
|
||||||
|
|
||||||
Node* CodeStubAssembler::JSReceiverToPrimitive(Node* context, Node* input) {
|
Node* CodeStubAssembler::JSReceiverToPrimitive(Node* context, Node* input) {
|
||||||
Label if_isreceiver(this, Label::kDeferred), if_isnotreceiver(this);
|
Label if_isreceiver(this, Label::kDeferred), if_isnotreceiver(this);
|
||||||
VARIABLE(result, MachineRepresentation::kTagged);
|
VARIABLE(result, MachineRepresentation::kTagged);
|
||||||
@ -4557,6 +4573,15 @@ Node* CodeStubAssembler::ToSmiLength(Node* input, Node* const context,
|
|||||||
return result.value();
|
return result.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node* CodeStubAssembler::ToLength_Inline(Node* const context,
|
||||||
|
Node* const input) {
|
||||||
|
Node* const smi_zero = SmiConstant(0);
|
||||||
|
return Select(
|
||||||
|
TaggedIsSmi(input), [=] { return SmiMax(input, smi_zero); },
|
||||||
|
[=] { return CallBuiltin(Builtins::kToLength, context, input); },
|
||||||
|
MachineRepresentation::kTagged);
|
||||||
|
}
|
||||||
|
|
||||||
Node* CodeStubAssembler::ToInteger(Node* context, Node* input,
|
Node* CodeStubAssembler::ToInteger(Node* context, Node* input,
|
||||||
ToIntegerTruncationMode mode) {
|
ToIntegerTruncationMode mode) {
|
||||||
// We might need to loop once for ToNumber conversion.
|
// We might need to loop once for ToNumber conversion.
|
||||||
|
@ -856,6 +856,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
|||||||
|
|
||||||
// Convert any object to a String.
|
// Convert any object to a String.
|
||||||
Node* ToString(Node* context, Node* input);
|
Node* ToString(Node* context, Node* input);
|
||||||
|
Node* ToString_Inline(Node* const context, Node* const input);
|
||||||
|
|
||||||
// Convert any object to a Primitive.
|
// Convert any object to a Primitive.
|
||||||
Node* JSReceiverToPrimitive(Node* context, Node* input);
|
Node* JSReceiverToPrimitive(Node* context, Node* input);
|
||||||
@ -871,6 +872,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
|||||||
// ES6 7.1.15 ToLength, but jumps to range_error if the result is not a Smi.
|
// ES6 7.1.15 ToLength, but jumps to range_error if the result is not a Smi.
|
||||||
Node* ToSmiLength(Node* input, Node* const context, Label* range_error);
|
Node* ToSmiLength(Node* input, Node* const context, Label* range_error);
|
||||||
|
|
||||||
|
// ES6 7.1.15 ToLength, but with inlined fast path.
|
||||||
|
Node* ToLength_Inline(Node* const context, Node* const input);
|
||||||
|
|
||||||
// Convert any object to an Integer.
|
// Convert any object to an Integer.
|
||||||
Node* ToInteger(Node* context, Node* input,
|
Node* ToInteger(Node* context, Node* input,
|
||||||
ToIntegerTruncationMode mode = kNoTruncation);
|
ToIntegerTruncationMode mode = kNoTruncation);
|
||||||
|
Loading…
Reference in New Issue
Block a user