Index: src/builtins/builtins-string-gen.cc |
diff --git a/src/builtins/builtins-string-gen.cc b/src/builtins/builtins-string-gen.cc |
index 18fbe9a72970654466cb9f1ac9620b13d64abdb2..9e76f110a57d8a9a7f723f9303cc7b8ce4740703 100644 |
--- a/src/builtins/builtins-string-gen.cc |
+++ b/src/builtins/builtins-string-gen.cc |
@@ -59,48 +59,74 @@ class StringBuiltinsAssembler : public CodeStubAssembler { |
return var_data.value(); |
} |
- Node* LoadOneByteChar(Node* string, Node* index) { |
- return Load(MachineType::Uint8(), string, OneByteCharOffset(index)); |
- } |
+ 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); |
- Node* OneByteCharAddress(Node* string, Node* index) { |
- Node* offset = OneByteCharOffset(index); |
- return IntPtrAdd(string, offset); |
- } |
+ // First combine the encodings. |
- Node* OneByteCharOffset(Node* index) { |
- return CharOffset(String::ONE_BYTE_ENCODING, index); |
- } |
+ 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* CharOffset(String::Encoding encoding, Node* index) { |
- const int header = SeqOneByteString::kHeaderSize - kHeapObjectTag; |
- Node* offset = index; |
- if (encoding == String::TWO_BYTE_ENCODING) { |
- offset = IntPtrAdd(offset, offset); |
- } |
- offset = IntPtrAdd(offset, IntPtrConstant(header)); |
- return offset; |
- } |
+ Node* const combined_encodings = |
+ Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1)); |
- void DispatchOnStringInstanceType(Node* const instance_type, |
- Label* if_onebyte_sequential, |
- Label* if_onebyte_external, |
- Label* if_otherwise) { |
- const int kMask = kStringRepresentationMask | kStringEncodingMask; |
- Node* const encoding_and_representation = |
- Word32And(instance_type, Int32Constant(kMask)); |
+ // Then dispatch on the combined encoding. |
+ |
+ Label unreachable(this, Label::kDeferred); |
int32_t values[] = { |
- kOneByteStringTag | kSeqStringTag, |
- kOneByteStringTag | kExternalStringTag, |
+ kOneByteStringTag | (kOneByteStringTag >> 1), |
+ kOneByteStringTag | (kTwoByteStringTag >> 1), |
+ kTwoByteStringTag | (kOneByteStringTag >> 1), |
+ kTwoByteStringTag | (kTwoByteStringTag >> 1), |
}; |
Label* labels[] = { |
- if_onebyte_sequential, if_onebyte_external, |
+ 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(); |
+ } |
- Switch(encoding_and_representation, if_otherwise, values, labels, |
- arraysize(values)); |
+ 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, |
+ 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 GenerateStringEqual(Node* context, Node* left, Node* right); |
@@ -113,8 +139,10 @@ class StringBuiltinsAssembler : public CodeStubAssembler { |
Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index, |
UnicodeEncoding encoding); |
- void StringIndexOf(Node* receiver, Node* instance_type, Node* search_string, |
- Node* search_string_instance_type, Node* position, |
+ 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); |
@@ -712,103 +740,148 @@ TF_BUILTIN(StringPrototypeConcat, CodeStubAssembler) { |
} |
void StringBuiltinsAssembler::StringIndexOf( |
- Node* receiver, Node* instance_type, Node* search_string, |
- Node* search_string_instance_type, Node* position, |
- std::function<void(Node*)> f_return) { |
- CSA_ASSERT(this, IsString(receiver)); |
+ 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) { |
+ CSA_ASSERT(this, IsString(subject_string)); |
CSA_ASSERT(this, IsString(search_string)); |
CSA_ASSERT(this, TaggedIsSmi(position)); |
- Label zero_length_needle(this), |
- call_runtime_unchecked(this, Label::kDeferred), return_minus_1(this), |
- check_search_string(this), continue_fast_path(this); |
- |
Node* const int_zero = IntPtrConstant(0); |
+ |
VARIABLE(var_needle_byte, MachineType::PointerRepresentation(), int_zero); |
VARIABLE(var_string_addr, MachineType::PointerRepresentation(), int_zero); |
- Node* needle_length = SmiUntag(LoadStringLength(search_string)); |
- // Use faster/complex runtime fallback for long search strings. |
- GotoIf(IntPtrLessThan(IntPtrConstant(1), needle_length), |
- &call_runtime_unchecked); |
- Node* string_length = SmiUntag(LoadStringLength(receiver)); |
- Node* start_position = IntPtrMax(SmiUntag(position), int_zero); |
+ Node* const search_length = SmiUntag(LoadStringLength(search_string)); |
+ Node* const subject_length = SmiUntag(LoadStringLength(subject_string)); |
+ Node* const start_position = IntPtrMax(SmiUntag(position), int_zero); |
- GotoIf(IntPtrEqual(int_zero, needle_length), &zero_length_needle); |
- // Check that the needle fits in the start position. |
- GotoIfNot(IntPtrLessThanOrEqual(needle_length, |
- IntPtrSub(string_length, start_position)), |
- &return_minus_1); |
- |
- // Load the string address. |
+ Label zero_length_needle(this), return_minus_1(this); |
{ |
- Label if_onebyte_sequential(this); |
- Label if_onebyte_external(this, Label::kDeferred); |
+ GotoIf(IntPtrEqual(int_zero, search_length), &zero_length_needle); |
+ |
+ // Check that the needle fits in the start position. |
+ GotoIfNot(IntPtrLessThanOrEqual(search_length, |
+ IntPtrSub(subject_length, start_position)), |
+ &return_minus_1); |
+ } |
- // Only support one-byte strings on the fast path. |
- DispatchOnStringInstanceType(instance_type, &if_onebyte_sequential, |
- &if_onebyte_external, &call_runtime_unchecked); |
+ // Try to unpack subject and search strings. Bail to runtime if either needs |
+ // to be flattened. |
+ ToDirectStringAssembler subject_to_direct(state(), subject_string); |
+ ToDirectStringAssembler search_to_direct(state(), search_string); |
- BIND(&if_onebyte_sequential); |
- { |
- var_string_addr.Bind( |
- OneByteCharAddress(BitcastTaggedToWord(receiver), start_position)); |
- Goto(&check_search_string); |
- } |
+ Label call_runtime_unchecked(this, Label::kDeferred); |
- BIND(&if_onebyte_external); |
- { |
- Node* const unpacked = TryDerefExternalString(receiver, instance_type, |
- &call_runtime_unchecked); |
- var_string_addr.Bind(OneByteCharAddress(unpacked, start_position)); |
- Goto(&check_search_string); |
- } |
- } |
+ subject_to_direct.TryToDirect(&call_runtime_unchecked); |
+ search_to_direct.TryToDirect(&call_runtime_unchecked); |
- // Load the needle character. |
- BIND(&check_search_string); |
- { |
- Label if_onebyte_sequential(this); |
- Label if_onebyte_external(this, Label::kDeferred); |
+ // Load pointers to string data. |
+ Node* const subject_ptr = |
+ subject_to_direct.PointerToData(&call_runtime_unchecked); |
+ Node* const search_ptr = |
+ search_to_direct.PointerToData(&call_runtime_unchecked); |
+ |
+ Node* const subject_offset = subject_to_direct.offset(); |
+ Node* const search_offset = search_to_direct.offset(); |
+ |
+ // Like String::IndexOf, the actual matching is done by the optimized |
+ // SearchString method in string-search.h. Dispatch based on string instance |
+ // types, then call straight into C++ for matching. |
- DispatchOnStringInstanceType(search_string_instance_type, |
- &if_onebyte_sequential, &if_onebyte_external, |
- &call_runtime_unchecked); |
+ CSA_ASSERT(this, IntPtrGreaterThan(search_length, int_zero)); |
+ CSA_ASSERT(this, IntPtrGreaterThanOrEqual(start_position, int_zero)); |
+ CSA_ASSERT(this, IntPtrGreaterThanOrEqual(subject_length, start_position)); |
+ CSA_ASSERT(this, |
+ IntPtrLessThanOrEqual(search_length, |
+ IntPtrSub(subject_length, start_position))); |
- BIND(&if_onebyte_sequential); |
+ Label one_one(this), one_two(this), two_one(this), two_two(this); |
+ DispatchOnStringEncodings(subject_to_direct.instance_type(), |
+ search_to_direct.instance_type(), &one_one, |
+ &one_two, &two_one, &two_two); |
+ |
+ typedef const uint8_t onebyte_t; |
+ typedef const uc16 twobyte_t; |
+ |
+ BIND(&one_one); |
+ { |
+ Node* const adjusted_subject_ptr = PointerToStringDataAtIndex( |
+ subject_ptr, subject_offset, String::ONE_BYTE_ENCODING); |
+ Node* const adjusted_search_ptr = PointerToStringDataAtIndex( |
+ search_ptr, search_offset, String::ONE_BYTE_ENCODING); |
+ |
+ Label direct_memchr_call(this), generic_fast_path(this); |
+ Branch(IntPtrEqual(search_length, IntPtrConstant(1)), &direct_memchr_call, |
+ &generic_fast_path); |
+ |
+ // An additional fast path that calls directly into memchr for 1-length |
+ // search strings. |
+ BIND(&direct_memchr_call); |
{ |
- var_needle_byte.Bind( |
- ChangeInt32ToIntPtr(LoadOneByteChar(search_string, int_zero))); |
- Goto(&continue_fast_path); |
+ Node* const string_addr = IntPtrAdd(adjusted_subject_ptr, start_position); |
+ Node* const search_length = IntPtrSub(subject_length, start_position); |
+ Node* const search_byte = |
+ ChangeInt32ToIntPtr(Load(MachineType::Uint8(), adjusted_search_ptr)); |
+ |
+ Node* const memchr = |
+ ExternalConstant(ExternalReference::libc_memchr_function(isolate())); |
+ Node* const result_address = |
+ CallCFunction3(MachineType::Pointer(), MachineType::Pointer(), |
+ MachineType::IntPtr(), MachineType::UintPtr(), memchr, |
+ string_addr, search_byte, search_length); |
+ GotoIf(WordEqual(result_address, int_zero), &return_minus_1); |
+ Node* const result_index = |
+ IntPtrAdd(IntPtrSub(result_address, string_addr), start_position); |
+ f_return(SmiTag(result_index)); |
} |
- BIND(&if_onebyte_external); |
+ BIND(&generic_fast_path); |
{ |
- Node* const unpacked = TryDerefExternalString( |
- search_string, search_string_instance_type, &call_runtime_unchecked); |
- var_needle_byte.Bind( |
- ChangeInt32ToIntPtr(LoadOneByteChar(unpacked, int_zero))); |
- Goto(&continue_fast_path); |
+ Node* const result = CallSearchStringRaw<onebyte_t, onebyte_t>( |
+ adjusted_subject_ptr, subject_length, adjusted_search_ptr, |
+ search_length, start_position); |
+ f_return(SmiTag(result)); |
} |
} |
- BIND(&continue_fast_path); |
+ BIND(&one_two); |
+ { |
+ Node* const adjusted_subject_ptr = PointerToStringDataAtIndex( |
+ subject_ptr, subject_offset, String::ONE_BYTE_ENCODING); |
+ Node* const adjusted_search_ptr = PointerToStringDataAtIndex( |
+ search_ptr, search_offset, String::TWO_BYTE_ENCODING); |
+ |
+ Node* const result = CallSearchStringRaw<onebyte_t, twobyte_t>( |
+ adjusted_subject_ptr, subject_length, adjusted_search_ptr, |
+ search_length, start_position); |
+ f_return(SmiTag(result)); |
+ } |
+ |
+ BIND(&two_one); |
+ { |
+ Node* const adjusted_subject_ptr = PointerToStringDataAtIndex( |
+ subject_ptr, subject_offset, String::TWO_BYTE_ENCODING); |
+ Node* const adjusted_search_ptr = PointerToStringDataAtIndex( |
+ search_ptr, search_offset, String::ONE_BYTE_ENCODING); |
+ |
+ Node* const result = CallSearchStringRaw<twobyte_t, onebyte_t>( |
+ adjusted_subject_ptr, subject_length, adjusted_search_ptr, |
+ search_length, start_position); |
+ f_return(SmiTag(result)); |
+ } |
+ |
+ BIND(&two_two); |
{ |
- Node* needle_byte = var_needle_byte.value(); |
- Node* string_addr = var_string_addr.value(); |
- Node* search_length = IntPtrSub(string_length, start_position); |
- // Call out to the highly optimized memchr to perform the actual byte |
- // search. |
- Node* memchr = |
- ExternalConstant(ExternalReference::libc_memchr_function(isolate())); |
- Node* result_address = |
- CallCFunction3(MachineType::Pointer(), MachineType::Pointer(), |
- MachineType::IntPtr(), MachineType::UintPtr(), memchr, |
- string_addr, needle_byte, search_length); |
- GotoIf(WordEqual(result_address, int_zero), &return_minus_1); |
- Node* result_index = |
- IntPtrAdd(IntPtrSub(result_address, string_addr), start_position); |
- f_return(SmiTag(result_index)); |
+ Node* const adjusted_subject_ptr = PointerToStringDataAtIndex( |
+ subject_ptr, subject_offset, String::TWO_BYTE_ENCODING); |
+ Node* const adjusted_search_ptr = PointerToStringDataAtIndex( |
+ search_ptr, search_offset, String::TWO_BYTE_ENCODING); |
+ |
+ Node* const result = CallSearchStringRaw<twobyte_t, twobyte_t>( |
+ adjusted_subject_ptr, subject_length, adjusted_search_ptr, |
+ search_length, start_position); |
+ f_return(SmiTag(result)); |
} |
BIND(&return_minus_1); |
@@ -817,7 +890,7 @@ void StringBuiltinsAssembler::StringIndexOf( |
BIND(&zero_length_needle); |
{ |
Comment("0-length search_string"); |
- f_return(SmiTag(IntPtrMin(string_length, start_position))); |
+ f_return(SmiTag(IntPtrMin(subject_length, start_position))); |
} |
BIND(&call_runtime_unchecked); |
@@ -826,7 +899,7 @@ void StringBuiltinsAssembler::StringIndexOf( |
// are already known due to type checks in this stub. |
Comment("Call Runtime Unchecked"); |
Node* result = CallRuntime(Runtime::kStringIndexOfUnchecked, SmiConstant(0), |
- receiver, search_string, position); |
+ subject_string, search_string, position); |
f_return(result); |
} |
} |