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);
} }
} }
......
...@@ -2,219 +2,167 @@ ...@@ -2,219 +2,167 @@
// 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-regexp-gen.h" #include "src/builtins/builtins-regexp-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-factory.h" #include "src/code-factory.h"
#include "src/code-stub-assembler.h"
#include "src/objects.h" #include "src/objects.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode; typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode;
typedef compiler::Node Node;
class StringBuiltinsAssembler : public CodeStubAssembler {
public: Node* StringBuiltinsAssembler::DirectStringData(Node* string,
explicit StringBuiltinsAssembler(compiler::CodeAssemblerState* state) Node* string_instance_type) {
: CodeStubAssembler(state) {} // Compute the effective offset of the first character.
VARIABLE(var_data, MachineType::PointerRepresentation());
// ES#sec-getsubstitution Label if_sequential(this), if_external(this), if_join(this);
Node* GetSubstitution(Node* context, Node* subject_string, Branch(Word32Equal(Word32And(string_instance_type,
Node* match_start_index, Node* match_end_index, Int32Constant(kStringRepresentationMask)),
Node* replace_string); Int32Constant(kSeqStringTag)),
&if_sequential, &if_external);
protected:
Node* DirectStringData(Node* string, Node* string_instance_type) { BIND(&if_sequential);
// Compute the effective offset of the first character. {
VARIABLE(var_data, MachineType::PointerRepresentation()); var_data.Bind(IntPtrAdd(
Label if_sequential(this), if_external(this), if_join(this); IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag),
Branch(Word32Equal(Word32And(string_instance_type, BitcastTaggedToWord(string)));
Int32Constant(kStringRepresentationMask)), Goto(&if_join);
Int32Constant(kSeqStringTag)),
&if_sequential, &if_external);
BIND(&if_sequential);
{
var_data.Bind(IntPtrAdd(
IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag),
BitcastTaggedToWord(string)));
Goto(&if_join);
}
BIND(&if_external);
{
// This is only valid for ExternalStrings where the resource data
// pointer is cached (i.e. no short external strings).
CSA_ASSERT(this, Word32NotEqual(
Word32And(string_instance_type,
Int32Constant(kShortExternalStringMask)),
Int32Constant(kShortExternalStringTag)));
var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset,
MachineType::Pointer()));
Goto(&if_join);
}
BIND(&if_join);
return var_data.value();
} }
void DispatchOnStringEncodings(Node* const lhs_instance_type, BIND(&if_external);
Node* const rhs_instance_type, {
Label* if_one_one, Label* if_one_two, // This is only valid for ExternalStrings where the resource data
Label* if_two_one, Label* if_two_two) { // pointer is cached (i.e. no short external strings).
STATIC_ASSERT(kStringEncodingMask == 0x8); CSA_ASSERT(
STATIC_ASSERT(kTwoByteStringTag == 0x0); this, Word32NotEqual(Word32And(string_instance_type,
STATIC_ASSERT(kOneByteStringTag == 0x8); Int32Constant(kShortExternalStringMask)),
Int32Constant(kShortExternalStringTag)));
// First combine the encodings. var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset,
MachineType::Pointer()));
Node* const encoding_mask = Int32Constant(kStringEncodingMask); Goto(&if_join);
Node* const lhs_encoding = Word32And(lhs_instance_type, encoding_mask); }
Node* const rhs_encoding = Word32And(rhs_instance_type, encoding_mask);
Node* const combined_encodings = BIND(&if_join);
Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1)); return var_data.value();
}
// Then dispatch on the combined encoding. void StringBuiltinsAssembler::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) {
STATIC_ASSERT(kStringEncodingMask == 0x8);
STATIC_ASSERT(kTwoByteStringTag == 0x0);
STATIC_ASSERT(kOneByteStringTag == 0x8);
Label unreachable(this, Label::kDeferred); // First combine the encodings.
int32_t values[] = { Node* const encoding_mask = Int32Constant(kStringEncodingMask);
kOneByteStringTag | (kOneByteStringTag >> 1), Node* const lhs_encoding = Word32And(lhs_instance_type, encoding_mask);
kOneByteStringTag | (kTwoByteStringTag >> 1), Node* const rhs_encoding = Word32And(rhs_instance_type, encoding_mask);
kTwoByteStringTag | (kOneByteStringTag >> 1),
kTwoByteStringTag | (kTwoByteStringTag >> 1),
};
Label* labels[] = {
if_one_one, if_one_two, if_two_one, if_two_two,
};
STATIC_ASSERT(arraysize(values) == arraysize(labels)); Node* const combined_encodings =
Switch(combined_encodings, &unreachable, values, labels, arraysize(values)); Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1));
BIND(&unreachable); // Then dispatch on the combined encoding.
Unreachable();
}
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* const function_addr = ExternalConstant(
ExternalReference::search_string_raw<SubjectChar, PatternChar>(
isolate()));
Node* const isolate_ptr =
ExternalConstant(ExternalReference::isolate_address(isolate()));
MachineType type_ptr = MachineType::Pointer();
MachineType type_intptr = MachineType::IntPtr();
Node* const result = CallCFunction6(
type_intptr, type_ptr, type_ptr, type_intptr, type_ptr, type_intptr,
type_intptr, function_addr, isolate_ptr, subject_ptr, subject_length,
search_ptr, search_length, start_position);
return result;
}
Node* PointerToStringDataAtIndex(Node* const string_data, Node* const index, Label unreachable(this, Label::kDeferred);
String::Encoding encoding) {
const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
? UINT8_ELEMENTS
: UINT16_ELEMENTS;
Node* const offset_in_bytes = int32_t values[] = {
ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS); kOneByteStringTag | (kOneByteStringTag >> 1),
return IntPtrAdd(string_data, offset_in_bytes); kOneByteStringTag | (kTwoByteStringTag >> 1),
} kTwoByteStringTag | (kOneByteStringTag >> 1),
kTwoByteStringTag | (kTwoByteStringTag >> 1),
};
Label* labels[] = {
if_one_one, if_one_two, if_two_one, if_two_two,
};
void GenerateStringEqual(Node* context, Node* left, Node* right); STATIC_ASSERT(arraysize(values) == arraysize(labels));
void GenerateStringRelationalComparison(Node* context, Node* left, Switch(combined_encodings, &unreachable, values, labels, arraysize(values));
Node* right,
RelationalComparisonMode mode);
Node* ToSmiBetweenZeroAnd(Node* context, Node* value, Node* limit); BIND(&unreachable);
Unreachable();
}
Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index, template <typename SubjectChar, typename PatternChar>
UnicodeEncoding encoding); Node* StringBuiltinsAssembler::CallSearchStringRaw(Node* const subject_ptr,
Node* const subject_length,
Node* const search_ptr,
Node* const search_length,
Node* const start_position) {
Node* const function_addr = ExternalConstant(
ExternalReference::search_string_raw<SubjectChar, PatternChar>(
isolate()));
Node* const isolate_ptr =
ExternalConstant(ExternalReference::isolate_address(isolate()));
MachineType type_ptr = MachineType::Pointer();
MachineType type_intptr = MachineType::IntPtr();
Node* const result = CallCFunction6(
type_intptr, type_ptr, type_ptr, type_intptr, type_ptr, type_intptr,
type_intptr, function_addr, isolate_ptr, subject_ptr, subject_length,
search_ptr, search_length, start_position);
return result;
}
void StringIndexOf(Node* const subject_string, Node* StringBuiltinsAssembler::PointerToStringDataAtIndex(
Node* const subject_instance_type, Node* const string_data, Node* const index, String::Encoding encoding) {
Node* const search_string, const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
Node* const search_instance_type, Node* const position, ? UINT8_ELEMENTS
std::function<void(Node*)> f_return); : UINT16_ELEMENTS;
Node* const offset_in_bytes =
ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS);
return IntPtrAdd(string_data, offset_in_bytes);
}
Node* IndexOfDollarChar(Node* const context, Node* const string); void StringBuiltinsAssembler::ConvertAndBoundsCheckStartArgument(
Node* context, Variable* var_start, Node* start, Node* string_length) {
Node* const start_int =
ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero);
Node* const zero = SmiConstant(Smi::kZero);
Node* IsNullOrUndefined(Node* const value); Label done(this);
void RequireObjectCoercible(Node* const context, Node* const value, Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
const char* method_name); Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber);
Node* SmiIsNegative(Node* const value) { BIND(&if_issmi);
return SmiLessThan(value, SmiConstant(0)); {
var_start->Bind(
Select(SmiLessThan(start_int, zero),
[&] { return SmiMax(SmiAdd(string_length, start_int), zero); },
[&] { return start_int; }, MachineRepresentation::kTagged));
Goto(&done);
} }
// substr and slice have a common way of handling the {start} argument. BIND(&if_isheapnumber);
void ConvertAndBoundsCheckStartArgument(Node* context, Variable* var_start, {
Node* start, Node* string_length) { // If {start} is a heap number, it is definitely out of bounds. If it is
Node* const start_int = // negative, {start} = max({string_length} + {start}),0) = 0'. If it is
ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero); // positive, set {start} to {string_length} which ultimately results in
Node* const zero = SmiConstant(Smi::kZero); // returning an empty string.
Node* const float_zero = Float64Constant(0.);
Label done(this); Node* const start_float = LoadHeapNumberValue(start_int);
Label if_issmi(this), if_isheapnumber(this, Label::kDeferred); var_start->Bind(SelectTaggedConstant(
Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber); Float64LessThan(start_float, float_zero), zero, string_length));
Goto(&done);
BIND(&if_issmi);
{
var_start->Bind(
Select(SmiLessThan(start_int, zero),
[&] { return SmiMax(SmiAdd(string_length, start_int), zero); },
[&] { return start_int; }, MachineRepresentation::kTagged));
Goto(&done);
}
BIND(&if_isheapnumber);
{
// If {start} is a heap number, it is definitely out of bounds. If it is
// negative, {start} = max({string_length} + {start}),0) = 0'. If it is
// positive, set {start} to {string_length} which ultimately results in
// returning an empty string.
Node* const float_zero = Float64Constant(0.);
Node* const start_float = LoadHeapNumberValue(start_int);
var_start->Bind(SelectTaggedConstant(
Float64LessThan(start_float, float_zero), zero, string_length));
Goto(&done);
}
BIND(&done);
} }
BIND(&done);
// 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);
};
void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left, void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left,
Node* right) { Node* right) {
// Here's pseudo-code for the algorithm below: // Here's pseudo-code for the algorithm below:
// //
// if (lhs == rhs) return true;
// if (lhs->length() != rhs->length()) return false; // if (lhs->length() != rhs->length()) return false;
// restart:
// if (lhs == rhs) return true;
// if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) { // if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) {
// return false; // return false;
// } // }
...@@ -225,33 +173,61 @@ void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left, ...@@ -225,33 +173,61 @@ void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left,
// return true; // return true;
// } // }
// if (lhs and/or rhs are indirect strings) { // if (lhs and/or rhs are indirect strings) {
// unwrap them and restart from the beginning; // unwrap them and restart from the "restart:" label;
// } // }
// return %StringEqual(lhs, rhs); // return %StringEqual(lhs, rhs);
VARIABLE(var_left, MachineRepresentation::kTagged, left); VARIABLE(var_left, MachineRepresentation::kTagged, left);
VARIABLE(var_right, MachineRepresentation::kTagged, right); VARIABLE(var_right, MachineRepresentation::kTagged, right);
Variable* input_vars[2] = {&var_left, &var_right}; Variable* input_vars[2] = {&var_left, &var_right};
Label if_equal(this), if_notequal(this), restart(this, 2, input_vars); Label if_equal(this), if_notequal(this), if_notbothdirectonebytestrings(this),
restart(this, 2, input_vars);
Node* lhs_length = LoadStringLength(left);
Node* rhs_length = LoadStringLength(right);
// Strings with different lengths cannot be equal.
GotoIf(WordNotEqual(lhs_length, rhs_length), &if_notequal);
Goto(&restart); Goto(&restart);
BIND(&restart); BIND(&restart);
Node* lhs = var_left.value(); Node* lhs = var_left.value();
Node* rhs = var_right.value(); Node* rhs = var_right.value();
// Fast check to see if {lhs} and {rhs} refer to the same String object. Node* lhs_instance_type = LoadInstanceType(lhs);
GotoIf(WordEqual(lhs, rhs), &if_equal); Node* rhs_instance_type = LoadInstanceType(rhs);
// Load the length of {lhs} and {rhs}. StringEqual_Core(context, lhs, lhs_instance_type, lhs_length, rhs,
Node* lhs_length = LoadStringLength(lhs); rhs_instance_type, &if_equal, &if_notequal,
Node* rhs_length = LoadStringLength(rhs); &if_notbothdirectonebytestrings);
// Strings with different lengths cannot be equal. BIND(&if_notbothdirectonebytestrings);
GotoIf(WordNotEqual(lhs_length, rhs_length), &if_notequal); {
// Try to unwrap indirect strings, restart the above attempt on success.
MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
rhs_instance_type, &restart);
// TODO(bmeurer): Add support for two byte string equality checks.
// Load instance types of {lhs} and {rhs}. TailCallRuntime(Runtime::kStringEqual, context, lhs, rhs);
Node* lhs_instance_type = LoadInstanceType(lhs); }
Node* rhs_instance_type = LoadInstanceType(rhs);
BIND(&if_equal);
Return(TrueConstant());
BIND(&if_notequal);
Return(FalseConstant());
}
void StringBuiltinsAssembler::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) {
CSA_ASSERT(this, IsString(lhs));
CSA_ASSERT(this, IsString(rhs));
CSA_ASSERT(this, WordEqual(LoadStringLength(lhs), lhs_length));
CSA_ASSERT(this, WordEqual(LoadStringLength(rhs), lhs_length));
// Fast check to see if {lhs} and {rhs} refer to the same String object.
GotoIf(WordEqual(lhs, rhs), if_equal);
// Combine the instance types into a single 16-bit value, so we can check // Combine the instance types into a single 16-bit value, so we can check
// both of them at once. // both of them at once.
...@@ -266,7 +242,7 @@ void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left, ...@@ -266,7 +242,7 @@ void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left,
GotoIf(Word32Equal(Word32And(both_instance_types, GotoIf(Word32Equal(Word32And(both_instance_types,
Int32Constant(kBothInternalizedMask)), Int32Constant(kBothInternalizedMask)),
Int32Constant(kBothInternalizedTag)), Int32Constant(kBothInternalizedTag)),
&if_notequal); if_not_equal);
// Check that both {lhs} and {rhs} are flat one-byte strings, and that // Check that both {lhs} and {rhs} are flat one-byte strings, and that
// in case of ExternalStrings the data pointer is cached.. // in case of ExternalStrings the data pointer is cached..
...@@ -277,61 +253,43 @@ void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left, ...@@ -277,61 +253,43 @@ void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left,
<< 8); << 8);
int const kBothDirectOneByteStringTag = int const kBothDirectOneByteStringTag =
kOneByteStringTag | (kOneByteStringTag << 8); kOneByteStringTag | (kOneByteStringTag << 8);
Label if_bothdirectonebytestrings(this), if_notbothdirectonebytestrings(this); GotoIfNot(Word32Equal(Word32And(both_instance_types,
Branch(Word32Equal(Word32And(both_instance_types, Int32Constant(kBothDirectOneByteStringMask)),
Int32Constant(kBothDirectOneByteStringMask)), Int32Constant(kBothDirectOneByteStringTag)),
Int32Constant(kBothDirectOneByteStringTag)), if_notbothdirectonebyte);
&if_bothdirectonebytestrings, &if_notbothdirectonebytestrings);
// At this point we know that we have two direct one-byte strings.
BIND(&if_bothdirectonebytestrings);
// Compute the effective offset of the first character.
Node* lhs_data = DirectStringData(lhs, lhs_instance_type);
Node* rhs_data = DirectStringData(rhs, rhs_instance_type);
// Compute the first offset after the string from the length.
Node* length = SmiUntag(lhs_length);
// Loop over the {lhs} and {rhs} strings to see if they are equal.
VARIABLE(var_offset, MachineType::PointerRepresentation());
Label loop(this, &var_offset);
var_offset.Bind(IntPtrConstant(0));
Goto(&loop);
BIND(&loop);
{ {
// Compute the effective offset of the first character. // If {offset} equals {end}, no difference was found, so the
Node* lhs_data = DirectStringData(lhs, lhs_instance_type); // strings are equal.
Node* rhs_data = DirectStringData(rhs, rhs_instance_type); Node* offset = var_offset.value();
GotoIf(WordEqual(offset, length), if_equal);
// Compute the first offset after the string from the length.
Node* length = SmiUntag(lhs_length);
// Loop over the {lhs} and {rhs} strings to see if they are equal.
VARIABLE(var_offset, MachineType::PointerRepresentation());
Label loop(this, &var_offset);
var_offset.Bind(IntPtrConstant(0));
Goto(&loop);
BIND(&loop);
{
// If {offset} equals {end}, no difference was found, so the
// strings are equal.
Node* offset = var_offset.value();
GotoIf(WordEqual(offset, length), &if_equal);
// Load the next characters from {lhs} and {rhs}. // Load the next characters from {lhs} and {rhs}.
Node* lhs_value = Load(MachineType::Uint8(), lhs_data, offset); Node* lhs_value = Load(MachineType::Uint8(), lhs_data, offset);
Node* rhs_value = Load(MachineType::Uint8(), rhs_data, offset); Node* rhs_value = Load(MachineType::Uint8(), rhs_data, offset);
// Check if the characters match. // Check if the characters match.
GotoIf(Word32NotEqual(lhs_value, rhs_value), &if_notequal); GotoIf(Word32NotEqual(lhs_value, rhs_value), if_not_equal);
// Advance to next character. // Advance to next character.
var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1))); var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1)));
Goto(&loop); Goto(&loop);
}
}
BIND(&if_notbothdirectonebytestrings);
{
// Try to unwrap indirect strings, restart the above attempt on success.
MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
rhs_instance_type, &restart);
// TODO(bmeurer): Add support for two byte string equality checks.
TailCallRuntime(Runtime::kStringEqual, context, lhs, rhs);
} }
BIND(&if_equal);
Return(TrueConstant());
BIND(&if_notequal);
Return(FalseConstant());
} }
void StringBuiltinsAssembler::GenerateStringRelationalComparison( void StringBuiltinsAssembler::GenerateStringRelationalComparison(
......
// 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