[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: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45332}
This commit is contained in:
Jakob Kummerow 2017-05-15 21:42:35 +02:00 committed by Commit Bot
parent 64fb9441fd
commit 1345de0b1e
6 changed files with 373 additions and 295 deletions

View File

@ -918,6 +918,7 @@ v8_source_set("v8_builtins_generators") {
"src/builtins/builtins-regexp-gen.h",
"src/builtins/builtins-sharedarraybuffer-gen.cc",
"src/builtins/builtins-string-gen.cc",
"src/builtins/builtins-string-gen.h",
"src/builtins/builtins-symbol-gen.cc",
"src/builtins/builtins-typedarray-gen.cc",
"src/builtins/builtins-utils-gen.h",

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/builtins/builtins-string-gen.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/code-stub-assembler.h"
@ -1703,32 +1704,37 @@ TF_BUILTIN(ArrayIndexOf, CodeStubAssembler) {
Node* array_length = SmiUntag(LoadJSArrayLength(array));
{
// For now only deal with undefined and Smis here; we must be really careful
// with side-effects from the ToInteger conversion as the side-effects might
// render our assumptions about the receiver being a fast JSArray and the
// length invalid.
Label done(this);
// Initialize fromIndex.
Label is_smi(this), is_nonsmi(this), done(this);
// If no fromIndex was passed, default to 0.
GotoIf(IntPtrLessThanOrEqual(argc, IntPtrConstant(kFromIndexArg)), &done);
// Handle Smis here and everything else in runtime.
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,
// as the side effects might render previously checked assumptions about
// the receiver being a fast JSArray and its length invalid.
Branch(TaggedIsSmi(start_from), &is_smi, &is_nonsmi);
Node* intptr_start_from = SmiUntag(start_from);
index_var.Bind(intptr_start_from);
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);
index_var.Bind(IntPtrMax(len_minus_start_from, intptr_zero));
GotoIfNot(IsUndefined(start_from), &call_runtime);
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);
}
BIND(&done);
}
@ -1736,27 +1742,29 @@ TF_BUILTIN(ArrayIndexOf, CodeStubAssembler) {
GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), array_length),
&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* element_kind_handlers[] = {&if_smiorobjects, &if_smiorobjects,
&if_smiorobjects, &if_smiorobjects,
&if_packed_doubles, &if_holey_doubles};
Node* map = LoadMap(array);
Node* elements_kind = LoadMapElementsKind(map);
Node* elements = LoadElements(array);
Switch(elements_kind, &return_not_found, kElementsKind, element_kind_handlers,
arraysize(kElementsKind));
STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
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);
{
VARIABLE(search_num, MachineRepresentation::kFloat64);
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);
search_num.Bind(SmiToFloat64(search_element));
@ -1814,22 +1822,35 @@ TF_BUILTIN(ArrayIndexOf, CodeStubAssembler) {
BIND(&string_loop);
{
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),
&return_not_found);
Node* element_k = LoadFixedArrayElement(elements, index_var.value());
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);
GotoIfNot(IsStringInstanceType(element_k_type), &continue_loop);
Branch(WordEqual(search_length, LoadStringLength(element_k)),
&slow_compare, &continue_loop);
// TODO(bmeurer): Consider inlining the StringEqual logic here.
Callable callable = CodeFactory::StringEqual(isolate());
Node* result = CallStub(callable, context, search_element, element_k);
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,
&continue_loop);
BIND(&continue_loop);
Increment(index_var);
Goto(&string_loop);
Goto(&next_iteration);
}
}

View File

@ -2,219 +2,167 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/builtins/builtins-string-gen.h"
#include "src/builtins/builtins-regexp-gen.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/code-factory.h"
#include "src/code-stub-assembler.h"
#include "src/objects.h"
namespace v8 {
namespace internal {
typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode;
typedef compiler::Node Node;
class StringBuiltinsAssembler : public CodeStubAssembler {
public:
explicit StringBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
Node* StringBuiltinsAssembler::DirectStringData(Node* string,
Node* string_instance_type) {
// Compute the effective offset of the first character.
VARIABLE(var_data, MachineType::PointerRepresentation());
Label if_sequential(this), if_external(this), if_join(this);
Branch(Word32Equal(Word32And(string_instance_type,
Int32Constant(kStringRepresentationMask)),
Int32Constant(kSeqStringTag)),
&if_sequential, &if_external);
// ES#sec-getsubstitution
Node* GetSubstitution(Node* context, Node* subject_string,
Node* match_start_index, Node* match_end_index,
Node* replace_string);
protected:
Node* DirectStringData(Node* string, Node* string_instance_type) {
// Compute the effective offset of the first character.
VARIABLE(var_data, MachineType::PointerRepresentation());
Label if_sequential(this), if_external(this), if_join(this);
Branch(Word32Equal(Word32And(string_instance_type,
Int32Constant(kStringRepresentationMask)),
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();
BIND(&if_sequential);
{
var_data.Bind(IntPtrAdd(
IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag),
BitcastTaggedToWord(string)));
Goto(&if_join);
}
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) {
STATIC_ASSERT(kStringEncodingMask == 0x8);
STATIC_ASSERT(kTwoByteStringTag == 0x0);
STATIC_ASSERT(kOneByteStringTag == 0x8);
// First combine the encodings.
Node* const encoding_mask = Int32Constant(kStringEncodingMask);
Node* const lhs_encoding = Word32And(lhs_instance_type, encoding_mask);
Node* const rhs_encoding = Word32And(rhs_instance_type, encoding_mask);
Node* const combined_encodings =
Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1));
// Then dispatch on the combined encoding.
Label unreachable(this, Label::kDeferred);
int32_t values[] = {
kOneByteStringTag | (kOneByteStringTag >> 1),
kOneByteStringTag | (kTwoByteStringTag >> 1),
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));
Switch(combined_encodings, &unreachable, values, labels, arraysize(values));
BIND(&unreachable);
Unreachable();
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);
}
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()));
BIND(&if_join);
return var_data.value();
}
MachineType type_ptr = MachineType::Pointer();
MachineType type_intptr = MachineType::IntPtr();
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);
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);
// First combine the encodings.
return result;
Node* const encoding_mask = Int32Constant(kStringEncodingMask);
Node* const lhs_encoding = Word32And(lhs_instance_type, encoding_mask);
Node* const rhs_encoding = Word32And(rhs_instance_type, encoding_mask);
Node* const combined_encodings =
Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1));
// Then dispatch on the combined encoding.
Label unreachable(this, Label::kDeferred);
int32_t values[] = {
kOneByteStringTag | (kOneByteStringTag >> 1),
kOneByteStringTag | (kTwoByteStringTag >> 1),
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));
Switch(combined_encodings, &unreachable, values, labels, arraysize(values));
BIND(&unreachable);
Unreachable();
}
template <typename SubjectChar, typename PatternChar>
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;
}
Node* StringBuiltinsAssembler::PointerToStringDataAtIndex(
Node* const string_data, Node* const index, String::Encoding encoding) {
const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
? UINT8_ELEMENTS
: UINT16_ELEMENTS;
Node* const offset_in_bytes =
ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS);
return IntPtrAdd(string_data, offset_in_bytes);
}
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);
Label done(this);
Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber);
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);
}
Node* PointerToStringDataAtIndex(Node* const string_data, Node* const index,
String::Encoding encoding) {
const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
? UINT8_ELEMENTS
: UINT16_ELEMENTS;
Node* const offset_in_bytes =
ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS);
return IntPtrAdd(string_data, offset_in_bytes);
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);
}
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));
}
// substr and slice have a common way of handling the {start} argument.
void 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);
Label done(this);
Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber);
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);
}
// 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);
};
BIND(&done);
}
void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left,
Node* right) {
// Here's pseudo-code for the algorithm below:
//
// if (lhs == rhs) return true;
// if (lhs->length() != rhs->length()) return false;
// restart:
// if (lhs == rhs) return true;
// if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) {
// return false;
// }
@ -225,97 +173,33 @@ void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left,
// return true;
// }
// 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);
VARIABLE(var_left, MachineRepresentation::kTagged, left);
VARIABLE(var_right, MachineRepresentation::kTagged, 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);
BIND(&restart);
Node* lhs = var_left.value();
Node* rhs = var_right.value();
// Fast check to see if {lhs} and {rhs} refer to the same String object.
GotoIf(WordEqual(lhs, rhs), &if_equal);
// Load the length of {lhs} and {rhs}.
Node* lhs_length = LoadStringLength(lhs);
Node* rhs_length = LoadStringLength(rhs);
// Strings with different lengths cannot be equal.
GotoIf(WordNotEqual(lhs_length, rhs_length), &if_notequal);
// Load instance types of {lhs} and {rhs}.
Node* lhs_instance_type = LoadInstanceType(lhs);
Node* rhs_instance_type = LoadInstanceType(rhs);
// Combine the instance types into a single 16-bit value, so we can check
// both of them at once.
Node* both_instance_types = Word32Or(
lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
// Check if both {lhs} and {rhs} are internalized. Since we already know
// that they're not the same object, they're not equal in that case.
int const kBothInternalizedMask =
kIsNotInternalizedMask | (kIsNotInternalizedMask << 8);
int const kBothInternalizedTag = kInternalizedTag | (kInternalizedTag << 8);
GotoIf(Word32Equal(Word32And(both_instance_types,
Int32Constant(kBothInternalizedMask)),
Int32Constant(kBothInternalizedTag)),
&if_notequal);
// Check that both {lhs} and {rhs} are flat one-byte strings, and that
// in case of ExternalStrings the data pointer is cached..
STATIC_ASSERT(kShortExternalStringTag != 0);
int const kBothDirectOneByteStringMask =
kStringEncodingMask | kIsIndirectStringMask | kShortExternalStringMask |
((kStringEncodingMask | kIsIndirectStringMask | kShortExternalStringMask)
<< 8);
int const kBothDirectOneByteStringTag =
kOneByteStringTag | (kOneByteStringTag << 8);
Label if_bothdirectonebytestrings(this), if_notbothdirectonebytestrings(this);
Branch(Word32Equal(Word32And(both_instance_types,
Int32Constant(kBothDirectOneByteStringMask)),
Int32Constant(kBothDirectOneByteStringTag)),
&if_bothdirectonebytestrings, &if_notbothdirectonebytestrings);
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);
{
// 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}.
Node* lhs_value = Load(MachineType::Uint8(), lhs_data, offset);
Node* rhs_value = Load(MachineType::Uint8(), rhs_data, offset);
// Check if the characters match.
GotoIf(Word32NotEqual(lhs_value, rhs_value), &if_notequal);
// Advance to next character.
var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1)));
Goto(&loop);
}
}
StringEqual_Core(context, lhs, lhs_instance_type, lhs_length, rhs,
rhs_instance_type, &if_equal, &if_notequal,
&if_notbothdirectonebytestrings);
BIND(&if_notbothdirectonebytestrings);
{
@ -334,6 +218,80 @@ void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left,
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
// both of them at once.
Node* both_instance_types = Word32Or(
lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
// Check if both {lhs} and {rhs} are internalized. Since we already know
// that they're not the same object, they're not equal in that case.
int const kBothInternalizedMask =
kIsNotInternalizedMask | (kIsNotInternalizedMask << 8);
int const kBothInternalizedTag = kInternalizedTag | (kInternalizedTag << 8);
GotoIf(Word32Equal(Word32And(both_instance_types,
Int32Constant(kBothInternalizedMask)),
Int32Constant(kBothInternalizedTag)),
if_not_equal);
// Check that both {lhs} and {rhs} are flat one-byte strings, and that
// in case of ExternalStrings the data pointer is cached..
STATIC_ASSERT(kShortExternalStringTag != 0);
int const kBothDirectOneByteStringMask =
kStringEncodingMask | kIsIndirectStringMask | kShortExternalStringMask |
((kStringEncodingMask | kIsIndirectStringMask | kShortExternalStringMask)
<< 8);
int const kBothDirectOneByteStringTag =
kOneByteStringTag | (kOneByteStringTag << 8);
GotoIfNot(Word32Equal(Word32And(both_instance_types,
Int32Constant(kBothDirectOneByteStringMask)),
Int32Constant(kBothDirectOneByteStringTag)),
if_notbothdirectonebyte);
// At this point we know that we have two direct one-byte strings.
// 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);
{
// 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}.
Node* lhs_value = Load(MachineType::Uint8(), lhs_data, offset);
Node* rhs_value = Load(MachineType::Uint8(), rhs_data, offset);
// Check if the characters match.
GotoIf(Word32NotEqual(lhs_value, rhs_value), if_not_equal);
// Advance to next character.
var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1)));
Goto(&loop);
}
}
void StringBuiltinsAssembler::GenerateStringRelationalComparison(
Node* context, Node* left, Node* right, RelationalComparisonMode mode) {
VARIABLE(var_left, MachineRepresentation::kTagged, left);

View File

@ -0,0 +1,95 @@
// 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_

View File

@ -319,6 +319,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(StringParseInt) \
V(StringCharCodeAtRT) \
V(StringIndexOfUnchecked) \
V(StringEqual) \
V(SymbolDescriptiveString) \
V(GenerateRandomNumbers) \
V(ExternalStringGetChar) \
@ -679,6 +680,7 @@ bool DebugEvaluate::FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info) {
if (builtin_index >= 0 && builtin_index < Builtins::builtin_count &&
BuiltinHasNoSideEffect(static_cast<Builtins::Name>(builtin_index))) {
#ifdef DEBUG
// TODO(yangguo): Check builtin-to-builtin calls too.
int mode = RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE);
bool failed = false;
for (RelocIterator it(info->code(), mode); !it.done(); it.next()) {

View File

@ -527,6 +527,7 @@
'builtins/builtins-sharedarraybuffer-gen.cc',
'builtins/builtins-string.cc',
'builtins/builtins-string-gen.cc',
'builtins/builtins-string-gen.h',
'builtins/builtins-intl.cc',
'builtins/builtins-intl-gen.cc',
'builtins/builtins-symbol.cc',