Commit 1345de0b authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[builtins] Tune Array.indexOf performance

BUG=v8:6371

Change-Id: Iacb4ad572ea83ade6262272ed30d4cb684f9d8ed
Reviewed-on: https://chromium-review.googlesource.com/505107
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45332}
parent 64fb9441
...@@ -918,6 +918,7 @@ v8_source_set("v8_builtins_generators") { ...@@ -918,6 +918,7 @@ v8_source_set("v8_builtins_generators") {
"src/builtins/builtins-regexp-gen.h", "src/builtins/builtins-regexp-gen.h",
"src/builtins/builtins-sharedarraybuffer-gen.cc", "src/builtins/builtins-sharedarraybuffer-gen.cc",
"src/builtins/builtins-string-gen.cc", "src/builtins/builtins-string-gen.cc",
"src/builtins/builtins-string-gen.h",
"src/builtins/builtins-symbol-gen.cc", "src/builtins/builtins-symbol-gen.cc",
"src/builtins/builtins-typedarray-gen.cc", "src/builtins/builtins-typedarray-gen.cc",
"src/builtins/builtins-utils-gen.h", "src/builtins/builtins-utils-gen.h",
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "src/builtins/builtins-string-gen.h"
#include "src/builtins/builtins-utils-gen.h" #include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h" #include "src/builtins/builtins.h"
#include "src/code-stub-assembler.h" #include "src/code-stub-assembler.h"
...@@ -1703,32 +1704,37 @@ TF_BUILTIN(ArrayIndexOf, CodeStubAssembler) { ...@@ -1703,32 +1704,37 @@ TF_BUILTIN(ArrayIndexOf, CodeStubAssembler) {
Node* array_length = SmiUntag(LoadJSArrayLength(array)); Node* array_length = SmiUntag(LoadJSArrayLength(array));
{ {
// For now only deal with undefined and Smis here; we must be really careful // Initialize fromIndex.
// with side-effects from the ToInteger conversion as the side-effects might Label is_smi(this), is_nonsmi(this), done(this);
// render our assumptions about the receiver being a fast JSArray and the
// length invalid.
Label done(this);
// If no fromIndex was passed, default to 0. // If no fromIndex was passed, default to 0.
GotoIf(IntPtrLessThanOrEqual(argc, IntPtrConstant(kFromIndexArg)), &done); GotoIf(IntPtrLessThanOrEqual(argc, IntPtrConstant(kFromIndexArg)), &done);
// Handle Smis here and everything else in runtime.
Node* start_from = args.AtIndex(kFromIndexArg); Node* start_from = args.AtIndex(kFromIndexArg);
GotoIfNot(TaggedIsSmi(start_from), &call_runtime); // Handle Smis and undefined here and everything else in runtime.
// We must be very careful with side effects from the ToInteger conversion,
Node* intptr_start_from = SmiUntag(start_from); // as the side effects might render previously checked assumptions about
index_var.Bind(intptr_start_from); // the receiver being a fast JSArray and its length invalid.
Branch(TaggedIsSmi(start_from), &is_smi, &is_nonsmi);
Label if_negative(this);
Branch(IntPtrLessThan(intptr_start_from, intptr_zero), &if_negative, &done);
BIND(&if_negative); BIND(&is_nonsmi);
{ {
Node* len_minus_start_from = IntPtrAdd(array_length, intptr_start_from); GotoIfNot(IsUndefined(start_from), &call_runtime);
index_var.Bind(IntPtrMax(len_minus_start_from, intptr_zero)); Goto(&done);
}
BIND(&is_smi);
{
Node* intptr_start_from = SmiUntag(start_from);
index_var.Bind(intptr_start_from);
GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done);
// The fromIndex is negative: add it to the array's length.
index_var.Bind(IntPtrAdd(array_length, index_var.value()));
// Clamp negative results at zero.
GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done);
index_var.Bind(intptr_zero);
Goto(&done); Goto(&done);
} }
BIND(&done); BIND(&done);
} }
...@@ -1736,27 +1742,29 @@ TF_BUILTIN(ArrayIndexOf, CodeStubAssembler) { ...@@ -1736,27 +1742,29 @@ TF_BUILTIN(ArrayIndexOf, CodeStubAssembler) {
GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), array_length), GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), array_length),
&return_not_found); &return_not_found);
static int32_t kElementsKind[] = {
FAST_SMI_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, FAST_ELEMENTS,
FAST_HOLEY_ELEMENTS, FAST_DOUBLE_ELEMENTS, FAST_HOLEY_DOUBLE_ELEMENTS,
};
Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this); Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this);
Label* element_kind_handlers[] = {&if_smiorobjects, &if_smiorobjects,
&if_smiorobjects, &if_smiorobjects,
&if_packed_doubles, &if_holey_doubles};
Node* map = LoadMap(array); Node* map = LoadMap(array);
Node* elements_kind = LoadMapElementsKind(map); Node* elements_kind = LoadMapElementsKind(map);
Node* elements = LoadElements(array); Node* elements = LoadElements(array);
Switch(elements_kind, &return_not_found, kElementsKind, element_kind_handlers, STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
arraysize(kElementsKind)); STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
STATIC_ASSERT(FAST_ELEMENTS == 2);
STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3);
GotoIf(
Uint32LessThanOrEqual(elements_kind, Int32Constant(FAST_HOLEY_ELEMENTS)),
&if_smiorobjects);
GotoIf(Word32Equal(elements_kind, Int32Constant(FAST_DOUBLE_ELEMENTS)),
&if_packed_doubles);
GotoIf(Word32Equal(elements_kind, Int32Constant(FAST_HOLEY_DOUBLE_ELEMENTS)),
&if_holey_doubles);
Goto(&return_not_found);
BIND(&if_smiorobjects); BIND(&if_smiorobjects);
{ {
VARIABLE(search_num, MachineRepresentation::kFloat64); VARIABLE(search_num, MachineRepresentation::kFloat64);
Label ident_loop(this, &index_var), heap_num_loop(this, &search_num), Label ident_loop(this, &index_var), heap_num_loop(this, &search_num),
string_loop(this, &index_var), not_smi(this), not_heap_num(this); string_loop(this), not_smi(this), not_heap_num(this);
GotoIfNot(TaggedIsSmi(search_element), &not_smi); GotoIfNot(TaggedIsSmi(search_element), &not_smi);
search_num.Bind(SmiToFloat64(search_element)); search_num.Bind(SmiToFloat64(search_element));
...@@ -1814,22 +1822,35 @@ TF_BUILTIN(ArrayIndexOf, CodeStubAssembler) { ...@@ -1814,22 +1822,35 @@ TF_BUILTIN(ArrayIndexOf, CodeStubAssembler) {
BIND(&string_loop); BIND(&string_loop);
{ {
CSA_ASSERT(this, IsString(search_element)); CSA_ASSERT(this, IsString(search_element));
Label continue_loop(this); Label continue_loop(this), next_iteration(this, &index_var),
slow_compare(this), runtime(this, Label::kDeferred);
Node* search_length = LoadStringLength(search_element);
Goto(&next_iteration);
BIND(&next_iteration);
GotoIfNot(UintPtrLessThan(index_var.value(), array_length), GotoIfNot(UintPtrLessThan(index_var.value(), array_length),
&return_not_found); &return_not_found);
Node* element_k = LoadFixedArrayElement(elements, index_var.value()); Node* element_k = LoadFixedArrayElement(elements, index_var.value());
GotoIf(TaggedIsSmi(element_k), &continue_loop); GotoIf(TaggedIsSmi(element_k), &continue_loop);
GotoIfNot(IsString(element_k), &continue_loop); GotoIf(WordEqual(search_element, element_k), &return_found);
Node* element_k_type = LoadInstanceType(element_k);
// TODO(bmeurer): Consider inlining the StringEqual logic here. GotoIfNot(IsStringInstanceType(element_k_type), &continue_loop);
Callable callable = CodeFactory::StringEqual(isolate()); Branch(WordEqual(search_length, LoadStringLength(element_k)),
Node* result = CallStub(callable, context, search_element, element_k); &slow_compare, &continue_loop);
BIND(&slow_compare);
StringBuiltinsAssembler string_asm(state());
string_asm.StringEqual_Core(context, search_element, search_type,
search_length, element_k, element_k_type,
&return_found, &continue_loop, &runtime);
BIND(&runtime);
Node* result = CallRuntime(Runtime::kStringEqual, context, search_element,
element_k);
Branch(WordEqual(BooleanConstant(true), result), &return_found, Branch(WordEqual(BooleanConstant(true), result), &return_found,
&continue_loop); &continue_loop);
BIND(&continue_loop); BIND(&continue_loop);
Increment(index_var); Increment(index_var);
Goto(&string_loop); Goto(&next_iteration);
} }
} }
......
This diff is collapsed.
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_BUILTINS_BUILTINS_STRING_GEN_H_
#define V8_BUILTINS_BUILTINS_STRING_GEN_H_
#include "src/code-stub-assembler.h"
namespace v8 {
namespace internal {
class StringBuiltinsAssembler : public CodeStubAssembler {
public:
explicit StringBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
// ES#sec-getsubstitution
Node* GetSubstitution(Node* context, Node* subject_string,
Node* match_start_index, Node* match_end_index,
Node* replace_string);
void StringEqual_Core(Node* context, Node* lhs, Node* lhs_instance_type,
Node* lhs_length, Node* rhs, Node* rhs_instance_type,
Label* if_equal, Label* if_not_equal,
Label* if_notbothdirectonebyte);
protected:
Node* DirectStringData(Node* string, Node* string_instance_type);
void DispatchOnStringEncodings(Node* const lhs_instance_type,
Node* const rhs_instance_type,
Label* if_one_one, Label* if_one_two,
Label* if_two_one, Label* if_two_two);
template <typename SubjectChar, typename PatternChar>
Node* CallSearchStringRaw(Node* const subject_ptr, Node* const subject_length,
Node* const search_ptr, Node* const search_length,
Node* const start_position);
Node* PointerToStringDataAtIndex(Node* const string_data, Node* const index,
String::Encoding encoding);
// substr and slice have a common way of handling the {start} argument.
void ConvertAndBoundsCheckStartArgument(Node* context, Variable* var_start,
Node* start, Node* string_length);
void GenerateStringEqual(Node* context, Node* left, Node* right);
void GenerateStringRelationalComparison(Node* context, Node* left,
Node* right,
RelationalComparisonMode mode);
Node* ToSmiBetweenZeroAnd(Node* context, Node* value, Node* limit);
Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index,
UnicodeEncoding encoding);
void StringIndexOf(Node* const subject_string,
Node* const subject_instance_type,
Node* const search_string,
Node* const search_instance_type, Node* const position,
std::function<void(Node*)> f_return);
Node* IndexOfDollarChar(Node* const context, Node* const string);
Node* IsNullOrUndefined(Node* const value);
void RequireObjectCoercible(Node* const context, Node* const value,
const char* method_name);
Node* SmiIsNegative(Node* const value) {
return SmiLessThan(value, SmiConstant(0));
}
// Implements boilerplate logic for {match, split, replace, search} of the
// form:
//
// if (!IS_NULL_OR_UNDEFINED(object)) {
// var maybe_function = object[symbol];
// if (!IS_UNDEFINED(maybe_function)) {
// return %_Call(maybe_function, ...);
// }
// }
//
// Contains fast paths for Smi and RegExp objects.
typedef std::function<Node*()> NodeFunction0;
typedef std::function<Node*(Node* fn)> NodeFunction1;
void MaybeCallFunctionAtSymbol(Node* const context, Node* const object,
Handle<Symbol> symbol,
const NodeFunction0& regexp_call,
const NodeFunction1& generic_call);
};
} // namespace internal
} // namespace v8
#endif // V8_BUILTINS_BUILTINS_STRING_GEN_H_
...@@ -319,6 +319,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { ...@@ -319,6 +319,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(StringParseInt) \ V(StringParseInt) \
V(StringCharCodeAtRT) \ V(StringCharCodeAtRT) \
V(StringIndexOfUnchecked) \ V(StringIndexOfUnchecked) \
V(StringEqual) \
V(SymbolDescriptiveString) \ V(SymbolDescriptiveString) \
V(GenerateRandomNumbers) \ V(GenerateRandomNumbers) \
V(ExternalStringGetChar) \ V(ExternalStringGetChar) \
...@@ -679,6 +680,7 @@ bool DebugEvaluate::FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info) { ...@@ -679,6 +680,7 @@ bool DebugEvaluate::FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info) {
if (builtin_index >= 0 && builtin_index < Builtins::builtin_count && if (builtin_index >= 0 && builtin_index < Builtins::builtin_count &&
BuiltinHasNoSideEffect(static_cast<Builtins::Name>(builtin_index))) { BuiltinHasNoSideEffect(static_cast<Builtins::Name>(builtin_index))) {
#ifdef DEBUG #ifdef DEBUG
// TODO(yangguo): Check builtin-to-builtin calls too.
int mode = RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE); int mode = RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE);
bool failed = false; bool failed = false;
for (RelocIterator it(info->code(), mode); !it.done(); it.next()) { for (RelocIterator it(info->code(), mode); !it.done(); it.next()) {
......
...@@ -527,6 +527,7 @@ ...@@ -527,6 +527,7 @@
'builtins/builtins-sharedarraybuffer-gen.cc', 'builtins/builtins-sharedarraybuffer-gen.cc',
'builtins/builtins-string.cc', 'builtins/builtins-string.cc',
'builtins/builtins-string-gen.cc', 'builtins/builtins-string-gen.cc',
'builtins/builtins-string-gen.h',
'builtins/builtins-intl.cc', 'builtins/builtins-intl.cc',
'builtins/builtins-intl-gen.cc', 'builtins/builtins-intl-gen.cc',
'builtins/builtins-symbol.cc', 'builtins/builtins-symbol.cc',
......
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